diff --git a/addOns/params/CHANGELOG.md b/addOns/params/CHANGELOG.md new file mode 100644 index 00000000000..136be566811 --- /dev/null +++ b/addOns/params/CHANGELOG.md @@ -0,0 +1,12 @@ +# Changelog +All notable changes to this add-on will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased +### Changed + +- Migrated from core. + +[0.1.0]: https://github.com/zaproxy/zap-extensions/releases/params-v0.0.1 diff --git a/addOns/params/gradle.properties b/addOns/params/gradle.properties new file mode 100644 index 00000000000..12f33ef6eb1 --- /dev/null +++ b/addOns/params/gradle.properties @@ -0,0 +1,2 @@ +version=0.0.1 +release=false diff --git a/addOns/params/params.gradle.kts b/addOns/params/params.gradle.kts new file mode 100644 index 00000000000..1a312172332 --- /dev/null +++ b/addOns/params/params.gradle.kts @@ -0,0 +1,40 @@ +import org.zaproxy.gradle.addon.AddOnStatus + +description = "Tracks parameters, cookies, and header values on a site by site basis." + +zapAddOn { + addOnName.set("Params") + addOnStatus.set(AddOnStatus.RELEASE) + + manifest { + author.set("ZAP Dev Team") + url.set("https://www.zaproxy.org/docs/desktop/addons/params/") + + dependencies { + addOns { + register("pscan") { + version.set(">= 0.1.0 & < 1.0.0") + } + } + } + } + + apiClientGen { + api.set("org.zaproxy.addon.params.ParamsAPI") + } +} + +crowdin { + configuration { + val path = "org/zaproxy/addon/params/resources/" + tokens.put("%messagesPath%", path) + tokens.put("%helpPath%", path) + } +} + +dependencies { + zapAddOn("pscan") + + testImplementation(project(":testutils")) + testImplementation(libs.log4j.core) +} diff --git a/addOns/params/src/main/java/org/zaproxy/addon/params/ExtensionParams2.java b/addOns/params/src/main/java/org/zaproxy/addon/params/ExtensionParams2.java new file mode 100644 index 00000000000..644dd069154 --- /dev/null +++ b/addOns/params/src/main/java/org/zaproxy/addon/params/ExtensionParams2.java @@ -0,0 +1,685 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2011 The ZAP Development Team + * + * 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.addon.params; + +import java.awt.EventQueue; +import java.util.Arrays; +import java.util.Collection; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.regex.Pattern; +import javax.swing.tree.TreeNode; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.parosproxy.paros.Constant; +import org.parosproxy.paros.control.Control; +import org.parosproxy.paros.control.Control.Mode; +import org.parosproxy.paros.core.scanner.NameValuePair; +import org.parosproxy.paros.core.scanner.VariantMultipartFormParameters; +import org.parosproxy.paros.db.DatabaseException; +import org.parosproxy.paros.db.RecordParam; +import org.parosproxy.paros.extension.ExtensionAdaptor; +import org.parosproxy.paros.extension.ExtensionHook; +import org.parosproxy.paros.extension.ExtensionHookView; +import org.parosproxy.paros.extension.ExtensionLoader; +import org.parosproxy.paros.extension.SessionChangedListener; +import org.parosproxy.paros.model.Model; +import org.parosproxy.paros.model.Session; +import org.parosproxy.paros.model.SiteNode; +import org.parosproxy.paros.network.HtmlParameter; +import org.parosproxy.paros.network.HttpHeader; +import org.parosproxy.paros.network.HttpHeaderField; +import org.parosproxy.paros.network.HttpMessage; +import org.zaproxy.addon.pscan.ExtensionPassiveScan2; +import org.zaproxy.zap.extension.anticsrf.ExtensionAntiCSRF; +import org.zaproxy.zap.extension.help.ExtensionHelp; +import org.zaproxy.zap.extension.httpsessions.ExtensionHttpSessions; +import org.zaproxy.zap.extension.search.ExtensionSearch; +import org.zaproxy.zap.utils.ThreadUtils; +import org.zaproxy.zap.view.SiteMapListener; +import org.zaproxy.zap.view.SiteMapTreeCellRenderer; + +public class ExtensionParams2 extends ExtensionAdaptor implements SiteMapListener { + + public static final String NAME = "ExtensionParams2"; + + private ParamsPanel paramsPanel = null; + private PopupMenuParamSearch popupMenuSearch = null; + private PopupMenuAddAntiCSRF popupMenuAddAntiCsrf = null; + private PopupMenuRemoveAntiCSRF popupMenuRemoveAntiCsrf = null; + private PopupMenuAddSession popupMenuAddSession = null; + private PopupMenuRemoveSession popupMenuRemoveSession = null; + private Map siteParamsMap = new HashMap<>(); + + private static final Logger LOGGER = LogManager.getLogger(ExtensionParams2.class); + + private ExtensionHttpSessions extensionHttpSessions; + private ParamScanner paramScanner; + + private boolean warnDbFull = true; + + public ExtensionParams2() { + super(NAME); + this.setOrder(59); + } + + // @Override + // public boolean isEnabled() { + // return false; + // } + + @Override + public boolean supportsDb(String type) { + return true; + } + + @Override + public String getUIName() { + return Constant.messages.getString("params.name"); + } + + @Override + public void hook(ExtensionHook extensionHook) { + super.hook(extensionHook); + extensionHook.addApiImplementor(new ParamsAPI(this)); + extensionHook.addSessionListener(new SessionChangedListenerImpl()); + extensionHook.addSiteMapListener(this); + + if (getView() != null) { + @SuppressWarnings("unused") + ExtensionHookView pv = extensionHook.getHookView(); + extensionHook.getHookView().addStatusPanel(getParamsPanel()); + + final ExtensionLoader extLoader = Control.getSingleton().getExtensionLoader(); + if (extLoader.isExtensionEnabled(ExtensionSearch.NAME)) { + extensionHook.getHookMenu().addPopupMenuItem(getPopupMenuParamSearch()); + } + + if (extLoader.isExtensionEnabled(ExtensionAntiCSRF.NAME)) { + extensionHook.getHookMenu().addPopupMenuItem(getPopupMenuAddAntiCSRF()); + extensionHook.getHookMenu().addPopupMenuItem(getPopupMenuRemoveAntiCSRF()); + } + + if (extLoader.isExtensionEnabled(ExtensionHttpSessions.NAME)) { + extensionHook.getHookMenu().addPopupMenuItem(getPopupMenuAddSession()); + extensionHook.getHookMenu().addPopupMenuItem(getPopupMenuRemoveSession()); + } + + ExtensionHelp.enableHelpKey(getParamsPanel(), "ui.tabs.params"); + } + + ExtensionPassiveScan2 extensionPassiveScan = + Control.getSingleton() + .getExtensionLoader() + .getExtension(ExtensionPassiveScan2.class); + if (extensionPassiveScan != null) { + paramScanner = new ParamScanner(this); + extensionPassiveScan.getPassiveScannersManager().add(paramScanner); + } + } + + @Override + public void unload() { + super.unload(); + + ExtensionPassiveScan2 extensionPassiveScan = + Control.getSingleton() + .getExtensionLoader() + .getExtension(ExtensionPassiveScan2.class); + if (extensionPassiveScan != null) { + extensionPassiveScan.getPassiveScannersManager().remove(paramScanner); + } + } + + private PopupMenuParamSearch getPopupMenuParamSearch() { + if (popupMenuSearch == null) { + popupMenuSearch = new PopupMenuParamSearch(); + popupMenuSearch.setExtension(this); + } + return popupMenuSearch; + } + + private PopupMenuAddAntiCSRF getPopupMenuAddAntiCSRF() { + if (popupMenuAddAntiCsrf == null) { + popupMenuAddAntiCsrf = new PopupMenuAddAntiCSRF(); + popupMenuAddAntiCsrf.setExtension(this); + } + return popupMenuAddAntiCsrf; + } + + private PopupMenuRemoveAntiCSRF getPopupMenuRemoveAntiCSRF() { + if (popupMenuRemoveAntiCsrf == null) { + popupMenuRemoveAntiCsrf = new PopupMenuRemoveAntiCSRF(); + popupMenuRemoveAntiCsrf.setExtension(this); + } + return popupMenuRemoveAntiCsrf; + } + + private PopupMenuAddSession getPopupMenuAddSession() { + if (popupMenuAddSession == null) { + popupMenuAddSession = new PopupMenuAddSession(); + popupMenuAddSession.setExtension(this); + } + return popupMenuAddSession; + } + + private PopupMenuRemoveSession getPopupMenuRemoveSession() { + if (popupMenuRemoveSession == null) { + popupMenuRemoveSession = new PopupMenuRemoveSession(); + popupMenuRemoveSession.setExtension(this); + } + return popupMenuRemoveSession; + } + + protected ParamsPanel getParamsPanel() { + if (paramsPanel == null) { + paramsPanel = new ParamsPanel(this); + } + return paramsPanel; + } + + /** + * Gets the ExtensionHttpSessions, if it's enabled + * + * @return the Http Sessions extension or null, if it's not available + */ + protected ExtensionHttpSessions getExtensionHttpSessions() { + if (extensionHttpSessions == null) { + extensionHttpSessions = + Control.getSingleton() + .getExtensionLoader() + .getExtension(ExtensionHttpSessions.class); + } + return extensionHttpSessions; + } + + public boolean onHttpRequestSend(HttpMessage msg) { + + // Check we know the site + String site = + msg.getRequestHeader().getHostName() + ":" + msg.getRequestHeader().getHostPort(); + + if (getView() != null) { + this.getParamsPanel().addSite(site); + } + + SiteParameters sps = this.siteParamsMap.get(site); + if (sps == null) { + sps = new SiteParameters(this, site); + this.siteParamsMap.put(site, sps); + } + + // Cookie Parameters + TreeSet params; + Iterator iter; + try { + params = msg.getRequestHeader().getCookieParams(); + iter = params.iterator(); + while (iter.hasNext()) { + persist(sps.addParam(site, iter.next(), msg)); + } + } catch (IllegalArgumentException e) { + LOGGER.warn("Failed to obtain the cookies: {}", e.getMessage(), e); + } + + // URL Parameters + params = msg.getUrlParams(); + iter = params.iterator(); + while (iter.hasNext()) { + persist(sps.addParam(site, iter.next(), msg)); + } + + // Form Parameters + // TODO flag anti csrf url ones too? + + ExtensionAntiCSRF extAntiCSRF = + Control.getSingleton().getExtensionLoader().getExtension(ExtensionAntiCSRF.class); + + params = msg.getFormParams(); + iter = params.iterator(); + HtmlParameter param; + while (iter.hasNext()) { + param = iter.next(); + if (extAntiCSRF != null && extAntiCSRF.isAntiCsrfToken(param.getName())) { + param.addFlag(HtmlParameter.Flags.anticsrf.name()); + } + persist(sps.addParam(site, param, msg)); + } + + VariantMultipartFormParameters params2 = new VariantMultipartFormParameters(); + params2.setMessage(msg); + for (NameValuePair nvp : params2.getParamList()) { + if (nvp.getType() == NameValuePair.TYPE_MULTIPART_DATA_PARAM + || nvp.getType() == NameValuePair.TYPE_MULTIPART_DATA_FILE_NAME) { + persist( + sps.addParam( + site, + new HtmlParameter( + HtmlParameter.Type.multipart, + nvp.getName(), + nvp.getValue()), + msg)); + } + } + + return true; + } + + private String setToString(Set set) { + StringBuilder sb = new StringBuilder(); + if (set == null) { + return ""; + } + // Despite the SonarLint warning we do need to sync on the set + synchronized (set) { + for (String str : set) { + if (sb.length() > 0) { + sb.append(','); + } + // Escape all commas in the values + sb.append(str.replace(",", "%2C")); + } + } + return sb.toString(); + } + + private void persist(HtmlParameterStats param) { + try { + if (param.getId() < 0) { + // Its a new one + RecordParam rp = + Model.getSingleton() + .getDb() + .getTableParam() + .insert( + param.getSite(), + param.getType().name(), + param.getName(), + param.getTimesUsed(), + setToString(param.getFlags()), + setToString(param.getValues())); + param.setId(rp.getParamId()); + } else { + // Its an existing one + Model.getSingleton() + .getDb() + .getTableParam() + .update( + param.getId(), + param.getTimesUsed(), + setToString(param.getFlags()), + setToString(param.getValues())); + } + } catch (DatabaseException e) { + if (hasCause(e, "truncation")) { + LOGGER.warn("Could not add or update param: {}", param.getName()); + LOGGER.warn( + "It is likely that the length of one of the data elements exceeded the column size."); + LOGGER.warn(e.getMessage()); + LOGGER.debug(e.getMessage(), e); + } else if (hasCause(e, "Data File size limit is reached")) { + if (warnDbFull) { + warnDbFull = false; + LOGGER.warn("Unable to persist parameter, database is full.", e); + } + } else { + LOGGER.error(e.getMessage(), e); + } + } + } + + private static boolean hasCause(Exception e, String wantedMessage) { + Throwable cause = e.getCause(); + if (cause == null) { + return false; + } + String message = cause.getMessage(); + if (message == null) { + return false; + } + return message.contains(wantedMessage); + } + + public boolean onHttpResponseReceive(HttpMessage msg) { + + // Check we know the site + String site = + msg.getRequestHeader().getHostName() + ":" + msg.getRequestHeader().getHostPort(); + + if (getView() != null) { + this.getParamsPanel().addSite(site); + } + + SiteParameters sps = this.getSiteParameters(site); + + // Cookie Parameters + try { + TreeSet params = msg.getResponseHeader().getCookieParams(); + Iterator iter = params.iterator(); + while (iter.hasNext()) { + persist(sps.addParam(site, iter.next(), msg)); + } + } catch (IllegalArgumentException e) { + LOGGER.warn("Failed to obtain the cookies: {}", e.getMessage(), e); + } + + // Header "Parameters" + List headersList = msg.getResponseHeader().getHeaders(); + List setCookieHeaders = + Arrays.asList( + HttpHeader.SET_COOKIE.toLowerCase(), HttpHeader.SET_COOKIE2.toLowerCase()); + for (HttpHeaderField hdrField : headersList) { + if (setCookieHeaders.contains(hdrField.getName().toLowerCase())) { + continue; + } + HtmlParameter headerParam = + new HtmlParameter( + HtmlParameter.Type.header, hdrField.getName(), hdrField.getValue()); + ThreadUtils.invokeLater(() -> persist(sps.addParam(site, headerParam, msg))); + } + + // TODO Only do if response URL different to request? + // URL Parameters + /* + params = msg.getUrlParams(); + iter = params.iterator(); + while (iter.hasNext()) { + sps.addParam(iter.next()); + } + */ + + return true; + } + + @Override + public void nodeSelected(SiteNode node) { + // Event from SiteMapListenner + this.getParamsPanel().nodeSelected(node); + } + + @Override + public void onReturnNodeRendererComponent( + SiteMapTreeCellRenderer component, boolean leaf, SiteNode value) {} + + protected void searchForSelectedParam() { + + HtmlParameterStats item = this.getParamsPanel().getSelectedParam(); + if (item != null) { + ExtensionSearch extSearch = + Control.getSingleton().getExtensionLoader().getExtension(ExtensionSearch.class); + + if (extSearch != null) { + if (HtmlParameter.Type.url.equals(item.getType())) { + extSearch.search( + "[?&]" + Pattern.quote(item.getName()) + "=.*", + ExtensionSearch.Type.URL, + true, + false); + } else if (HtmlParameter.Type.cookie.equals(item.getType())) { + extSearch.search( + Pattern.quote(item.getName()) + "=.*", + ExtensionSearch.Type.Header, + true, + false); + } else if (HtmlParameter.Type.header.equals(item.getType())) { + extSearch.search( + Pattern.quote(item.getName()) + ":.*", + ExtensionSearch.Type.Header, + true, + false); + } else if (HtmlParameter.Type.multipart.equals(item.getType())) { + extSearch.search( + "(?i)\\s*content-disposition\\s*:.*\\s+name\\s*\\=?\\s*\\\"?" + + Pattern.quote(item.getName()), + ExtensionSearch.Type.Request, + true, + false); + } else { + // FORM + extSearch.search( + Pattern.quote(item.getName()) + "=.*", + ExtensionSearch.Type.Request, + true, + false); + } + } + } + } + + public void addAntiCsrfToken() { + HtmlParameterStats item = this.getParamsPanel().getSelectedParam(); + + ExtensionAntiCSRF extAntiCSRF = + Control.getSingleton().getExtensionLoader().getExtension(ExtensionAntiCSRF.class); + + if (extAntiCSRF != null && item != null) { + extAntiCSRF.addAntiCsrfTokenName(item.getName()); + item.addFlag(HtmlParameter.Flags.anticsrf.name()); + // Repaint so change shows up + this.getParamsPanel().getParamsTable().repaint(); + + // Dont think we need to do this... at least until rescan option implemented ... + // Control.getSingleton().getMenuToolsControl().options(Constant.messages.getString("options.acsrf.title")); + + } + } + + public void removeAntiCsrfToken() { + HtmlParameterStats item = this.getParamsPanel().getSelectedParam(); + + ExtensionAntiCSRF extAntiCSRF = + Control.getSingleton().getExtensionLoader().getExtension(ExtensionAntiCSRF.class); + + if (extAntiCSRF != null && item != null) { + extAntiCSRF.removeAntiCsrfTokenName(item.getName()); + item.removeFlag(HtmlParameter.Flags.anticsrf.name()); + // Repaint so change shows up + this.getParamsPanel().getParamsTable().repaint(); + + // Dont think we need to do this... at least until rescan option implemented ... + // Control.getSingleton().getMenuToolsControl().options(Constant.messages.getString("options.acsrf.title")); + } + } + + /** + * Tells whether or not the given {@code site} was already seen. + * + * @param site the site that will be checked + * @return {@code true} if the given {@code site} was already seen, {@code false} otherwise. + * @since 2.5.0 + * @see #hasParameters(String) + */ + public boolean hasSite(String site) { + return siteParamsMap.containsKey(site); + } + + /** + * Tells whether or not the given {@code site} has parameters. + * + * @param site the site that will be checked + * @return {@code true} if the given {@code site} has parameters, {@code false} if not, or was + * not yet seen. + * @since 2.5.0 + * @see #hasSite(String) + */ + public boolean hasParameters(String site) { + SiteParameters siteParameters = siteParamsMap.get(site); + if (siteParameters == null) { + return false; + } + return siteParameters.hasParams(); + } + + public SiteParameters getSiteParameters(String site) { + SiteParameters sps = this.siteParamsMap.get(site); + if (sps == null) { + sps = new SiteParameters(this, site); + siteParamsMap.put(site, sps); + } + return sps; + } + + public Collection getAllSiteParameters() { + return this.siteParamsMap.values(); + } + + /** + * Adds a new session token from the selected parameter. Also notifies the {@link + * ExtensionHttpSessions} if it's active. + */ + public void addSessionToken() { + // Get the selected parameter + HtmlParameterStats item = this.getParamsPanel().getSelectedParam(); + if (item != null) { + + // If the HttpSessions extension is active, notify it of the new session token + ExtensionHttpSessions extSession = this.getExtensionHttpSessions(); + if (extSession != null) { + extSession.addHttpSessionToken( + this.getParamsPanel().getCurrentSite(), item.getName()); + } + + // Flag the item accordingly + item.addFlag(HtmlParameter.Flags.session.name()); + // Repaint so change shows up + this.getParamsPanel().getParamsTable().repaint(); + } + } + + /** + * Removes the currently selected parameter as a session token. Also notifies the {@link + * ExtensionHttpSessions} if it's active. + */ + public void removeSessionToken() { + HtmlParameterStats item = this.getParamsPanel().getSelectedParam(); + + if (item != null) { + // If the HttpSessions extension is active, notify it of the removed session token + ExtensionHttpSessions extSession = this.getExtensionHttpSessions(); + if (extSession != null) { + extSession.removeHttpSessionToken( + this.getParamsPanel().getCurrentSite(), item.getName()); + } + + // Unflag the item accordingly + item.removeFlag(HtmlParameter.Flags.session.name()); + // Repaint so change shows up + this.getParamsPanel().getParamsTable().repaint(); + } + } + + public HtmlParameterStats getSelectedParam() { + return this.getParamsPanel().getSelectedParam(); + } + + @Override + public String getAuthor() { + return Constant.ZAP_TEAM; + } + + @Override + public String getDescription() { + return Constant.messages.getString("params.desc"); + } + + private class SessionChangedListenerImpl implements SessionChangedListener { + + @Override + public void sessionAboutToChange(Session session) { + if (hasView()) { + getParamsPanel().reset(); + } + } + + @Override + public void sessionChanged(final Session session) { + warnDbFull = true; + + if (EventQueue.isDispatchThread()) { + sessionChangedEventHandler(session); + + } else { + try { + EventQueue.invokeAndWait( + new Runnable() { + @Override + public void run() { + sessionChangedEventHandler(session); + } + }); + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + } + } + if (hasView()) { + ThreadUtils.invokeAndWaitHandled(getParamsPanel()::reset); + } + } + + private void sessionChangedEventHandler(Session session) { + // Clear all scans + siteParamsMap = new HashMap<>(); + if (getView() != null) { + getParamsPanel().reset(); + } + if (session == null) { + // Closedown + return; + } + + // Repopulate + SiteNode root = session.getSiteTree().getRoot(); + @SuppressWarnings("unchecked") + Enumeration en = root.children(); + while (en.hasMoreElements()) { + String site = ((SiteNode) en.nextElement()).getNodeName(); + if (getView() != null) { + getParamsPanel().addSite(site); + } + } + + try { + List params = Model.getSingleton().getDb().getTableParam().getAll(); + + for (RecordParam param : params) { + SiteParameters sps = getSiteParameters(param.getSite()); + sps.addParam(param.getSite(), param); + } + } catch (DatabaseException e) { + LOGGER.error(e.getMessage(), e); + } + } + + @Override + public void sessionScopeChanged(Session session) { + // Nothing to do + } + + @Override + public void sessionModeChanged(Mode mode) { + // Nothing to do + } + } +} diff --git a/addOns/params/src/main/java/org/zaproxy/addon/params/HtmlParameterStats.java b/addOns/params/src/main/java/org/zaproxy/addon/params/HtmlParameterStats.java new file mode 100644 index 00000000000..93b951ef705 --- /dev/null +++ b/addOns/params/src/main/java/org/zaproxy/addon/params/HtmlParameterStats.java @@ -0,0 +1,163 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2011 The ZAP Development Team + * + * 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.addon.params; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import org.parosproxy.paros.network.HtmlParameter; + +public class HtmlParameterStats implements Comparable { + private long id = -1; + private String site; + private String name; + private HtmlParameter.Type type; + private int timesUsed = 0; + private Set flags = Collections.synchronizedSet(new HashSet<>()); + private Set values = Collections.synchronizedSet(new HashSet<>()); + + public HtmlParameterStats( + String site, String name, HtmlParameter.Type type, String value, Set flags) { + this.site = site; + this.name = name; + this.type = type; + this.addValue(value); + this.flags = flags; + this.incTimesUsed(); + } + + public HtmlParameterStats( + long id, + String site, + String name, + String type, + int timesUsed, + Set values, + Set flags) { + this.id = id; + this.site = site; + this.name = name; + this.type = HtmlParameter.Type.valueOf(type); + this.timesUsed = timesUsed; + this.values = values; + this.flags = flags; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getSite() { + return site; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public HtmlParameter.Type getType() { + return type; + } + + public void setType(HtmlParameter.Type type) { + this.type = type; + } + + public int getTimesUsed() { + return timesUsed; + } + + public void incTimesUsed() { + this.timesUsed++; + } + + public Set getValues() { + return values; + } + + public void addValue(String value) { + if (value == null) { + value = ""; + } + this.values.add(value); + } + + public String getValuesSummary() { + StringBuilder sb = new StringBuilder(); + for (String value : values) { + if (sb.length() > 0) { + sb.append(", "); + } + sb.append(value); + if (sb.length() > 250) { + break; + } + } + return sb.toString(); + } + + public Set getFlags() { + return flags; + } + + public void addFlag(String flag) { + this.flags.add(flag); + } + + public String getAllFlags() { + StringBuilder sb = new StringBuilder(); + for (String flag : flags) { + if (sb.length() > 0) { + sb.append(", "); + } + sb.append(flag); + if (sb.length() > 250) { + break; + } + } + + return sb.toString(); + } + + @Override + public int compareTo(HtmlParameterStats o) { + if (o == null) { + return 1; + } + int result = this.type.ordinal() - o.getType().ordinal(); + if (result == 0) { + // Same type + result = this.name.compareTo(o.getName()); + } + return result; + } + + public void removeFlag(String flag) { + this.flags.remove(flag); + } +} diff --git a/addOns/params/src/main/java/org/zaproxy/addon/params/ParamScanner.java b/addOns/params/src/main/java/org/zaproxy/addon/params/ParamScanner.java new file mode 100644 index 00000000000..cfe1d5d59e3 --- /dev/null +++ b/addOns/params/src/main/java/org/zaproxy/addon/params/ParamScanner.java @@ -0,0 +1,65 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2011 The ZAP Development Team + * + * 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.addon.params; + +import net.htmlparser.jericho.Source; +import org.parosproxy.paros.network.HttpMessage; +import org.zaproxy.zap.extension.pscan.PassiveScanner; +import org.zaproxy.zap.extension.pscan.PluginPassiveScanner; + +public class ParamScanner implements PassiveScanner { + + private final ExtensionParams2 extParams; + + public ParamScanner(ExtensionParams2 extParams) { + this.extParams = extParams; + } + + @Override + public void scanHttpRequestSend(HttpMessage msg, int id) { + extParams.onHttpRequestSend(msg); + } + + @Override + public void scanHttpResponseReceive(HttpMessage msg, int id, Source source) { + extParams.onHttpResponseReceive(msg); + } + + @Override + public String getName() { + return "Parameter Scanner"; + } + + @Override + public boolean isEnabled() { + // Always enabled + return true; + } + + @Override + public void setEnabled(boolean enabled) { + // Ignore + } + + @Override + public boolean appliesToHistoryType(int historyType) { + return PluginPassiveScanner.getDefaultHistoryTypes().contains(historyType); + } +} diff --git a/addOns/params/src/main/java/org/zaproxy/addon/params/ParamsAPI.java b/addOns/params/src/main/java/org/zaproxy/addon/params/ParamsAPI.java new file mode 100644 index 00000000000..b1266ea5901 --- /dev/null +++ b/addOns/params/src/main/java/org/zaproxy/addon/params/ParamsAPI.java @@ -0,0 +1,113 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2011 The ZAP Development Team + * + * 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.addon.params; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import net.sf.json.JSONObject; +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.utils.ApiUtils; + +public class ParamsAPI extends ApiImplementor { + + private static final String PREFIX = "params"; + private static final String VIEW_PARAMS = "params"; + private static final String VIEW_PARAMS_PARAM_SITE = "site"; + + private ExtensionParams2 extension; + + public ParamsAPI(ExtensionParams2 extension) { + this.extension = extension; + this.addApiView( + new ApiView(VIEW_PARAMS, new String[] {}, new String[] {VIEW_PARAMS_PARAM_SITE})); + } + + @Override + public String getPrefix() { + return PREFIX; + } + + @Override + public ApiResponse handleApiView(String name, JSONObject params) throws ApiException { + if (VIEW_PARAMS.equals(name)) { + ApiResponseList result = new ApiResponseList("Parameters"); + if (params.containsKey(VIEW_PARAMS_PARAM_SITE)) { + String paramSite = params.getString(VIEW_PARAMS_PARAM_SITE); + if (!paramSite.isEmpty()) { + String site = ApiUtils.getAuthority(paramSite); + if (!extension.hasSite(site)) { + throw new ApiException(ApiException.Type.DOES_NOT_EXIST, paramSite); + } + + if (extension.hasParameters(site)) { + result.addItem( + createSiteParamStatsResponse(extension.getSiteParameters(site))); + } + return result; + } + } + + Collection siteParams = extension.getAllSiteParameters(); + for (SiteParameters siteParam : siteParams) { + result.addItem(createSiteParamStatsResponse(siteParam)); + } + return result; + + } else { + throw new ApiException(ApiException.Type.BAD_VIEW); + } + } + + private static ApiResponseList createSiteParamStatsResponse(SiteParameters siteParam) { + ApiResponseList stats = new ApiResponseList("Parameter"); + for (HtmlParameterStats param : siteParam.getParams()) { + Map map = new HashMap<>(); + map.put("site", param.getSite()); + map.put("name", param.getName()); + map.put("type", param.getType().name()); + map.put("timesUsed", String.valueOf(param.getTimesUsed())); + stats.addItem(new ApiResponseSet<>("Stats", map)); + + ApiResponseList flags = new ApiResponseList("Flags"); + for (String flag : param.getFlags()) { + flags.addItem(new ApiResponseElement("Flag", flag)); + } + if (param.getFlags().size() > 0) { + stats.addItem(flags); + } + + ApiResponseList vals = new ApiResponseList("Values"); + for (String value : param.getValues()) { + vals.addItem(new ApiResponseElement("Value", value)); + } + if (param.getValues().size() > 0) { + stats.addItem(vals); + } + } + return stats; + } +} diff --git a/addOns/params/src/main/java/org/zaproxy/addon/params/ParamsPanel.java b/addOns/params/src/main/java/org/zaproxy/addon/params/ParamsPanel.java new file mode 100644 index 00000000000..a74f8fcdbf0 --- /dev/null +++ b/addOns/params/src/main/java/org/zaproxy/addon/params/ParamsPanel.java @@ -0,0 +1,397 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2011 The ZAP Development Team + * + * 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.addon.params; + +import java.awt.CardLayout; +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.event.KeyEvent; +import javax.swing.ImageIcon; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JToolBar; +import javax.swing.table.DefaultTableColumnModel; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; +import org.jdesktop.swingx.JXTable; +import org.parosproxy.paros.Constant; +import org.parosproxy.paros.extension.AbstractPanel; +import org.parosproxy.paros.model.SiteNode; +import org.parosproxy.paros.network.HtmlParameter; +import org.parosproxy.paros.view.View; +import org.zaproxy.zap.utils.SortedComboBoxModel; +import org.zaproxy.zap.utils.TableExportButton; +import org.zaproxy.zap.view.ScanPanel; + +@SuppressWarnings("serial") +public class ParamsPanel extends AbstractPanel { + + private static final long serialVersionUID = 1L; + + public static final String PANEL_NAME = "params"; + + private ExtensionParams2 extension = null; + private JPanel panelCommand = null; + private JToolBar panelToolbar = null; + private JScrollPane jScrollPane = null; + + private String currentSite = null; + private JComboBox siteSelect = null; + private SortedComboBoxModel siteModel = new SortedComboBoxModel<>(); + // private JButton optionsButton = null; + + private JXTable paramsTable = null; + private ParamsTableModel paramsModel = new ParamsTableModel(); + private TableExportButton exportButton = null; + + // private static Log log = LogFactory.getLog(ParamsPanel.class); + + public ParamsPanel(ExtensionParams2 extension) { + super(); + this.extension = extension; + initialize(); + } + + /** This method initializes this */ + private void initialize() { + this.setLayout(new CardLayout()); + this.setSize(474, 251); + this.setName(Constant.messages.getString("params.panel.title")); + this.setIcon( + new ImageIcon( + ParamsPanel.class.getResource("/resource/icon/16/179.png"))); // 'form' icon + this.setDefaultAccelerator( + extension + .getView() + .getMenuShortcutKeyStroke(KeyEvent.VK_P, KeyEvent.SHIFT_DOWN_MASK, false)); + this.setMnemonic(Constant.messages.getChar("params.panel.mnemonic")); + this.add(getPanelCommand(), getPanelCommand().getName()); + } + + /** + * This method initializes panelCommand + * + * @return javax.swing.JPanel + */ + private javax.swing.JPanel getPanelCommand() { + if (panelCommand == null) { + + panelCommand = new javax.swing.JPanel(); + panelCommand.setLayout(new java.awt.GridBagLayout()); + panelCommand.setName("Params"); + + GridBagConstraints gridBagConstraints1 = new GridBagConstraints(); + GridBagConstraints gridBagConstraints2 = new GridBagConstraints(); + + gridBagConstraints1.gridx = 0; + gridBagConstraints1.gridy = 0; + gridBagConstraints1.insets = new java.awt.Insets(2, 2, 2, 2); + gridBagConstraints1.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints1.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints1.weightx = 1.0D; + gridBagConstraints2.gridx = 0; + gridBagConstraints2.gridy = 1; + gridBagConstraints2.weightx = 1.0; + gridBagConstraints2.weighty = 1.0; + gridBagConstraints2.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints2.insets = new java.awt.Insets(0, 0, 0, 0); + gridBagConstraints2.anchor = java.awt.GridBagConstraints.NORTHWEST; + + panelCommand.add(this.getPanelToolbar(), gridBagConstraints1); + panelCommand.add(getJScrollPane(), gridBagConstraints2); + } + return panelCommand; + } + + private javax.swing.JToolBar getPanelToolbar() { + if (panelToolbar == null) { + + panelToolbar = new javax.swing.JToolBar(); + panelToolbar.setLayout(new java.awt.GridBagLayout()); + panelToolbar.setEnabled(true); + panelToolbar.setFloatable(false); + panelToolbar.setRollover(true); + panelToolbar.setPreferredSize(new java.awt.Dimension(800, 30)); + panelToolbar.setName("ParamsToolbar"); + + GridBagConstraints gridBagConstraints0 = new GridBagConstraints(); + GridBagConstraints gridBagConstraints1 = new GridBagConstraints(); + GridBagConstraints gridBagConstraints2 = new GridBagConstraints(); + GridBagConstraints gridBagConstraints3 = new GridBagConstraints(); + GridBagConstraints gridBagConstraintsx = new GridBagConstraints(); + + gridBagConstraints0.gridx = 0; + gridBagConstraints0.gridy = 0; + gridBagConstraints0.insets = new java.awt.Insets(0, 0, 0, 0); + gridBagConstraints0.anchor = java.awt.GridBagConstraints.WEST; + + gridBagConstraints1.gridx = 1; + gridBagConstraints1.gridy = 0; + gridBagConstraints1.insets = new java.awt.Insets(0, 0, 0, 0); + gridBagConstraints1.anchor = java.awt.GridBagConstraints.WEST; + + gridBagConstraints2.gridx = 2; + gridBagConstraints2.gridy = 0; + gridBagConstraints2.insets = new java.awt.Insets(0, 0, 0, 0); + gridBagConstraints2.anchor = java.awt.GridBagConstraints.WEST; + + gridBagConstraints3.gridx = 3; + gridBagConstraints3.gridy = 0; + gridBagConstraints3.insets = new java.awt.Insets(0, 0, 0, 0); + gridBagConstraints3.anchor = java.awt.GridBagConstraints.WEST; + + gridBagConstraintsx.gridx = 4; + gridBagConstraintsx.gridy = 0; + gridBagConstraintsx.weightx = 1.0; + gridBagConstraintsx.weighty = 1.0; + gridBagConstraintsx.insets = new java.awt.Insets(0, 0, 0, 0); + gridBagConstraintsx.anchor = java.awt.GridBagConstraints.EAST; + gridBagConstraintsx.fill = java.awt.GridBagConstraints.HORIZONTAL; + + JLabel t1 = new JLabel(); + + // panelToolbar.add(getOptionsButton(), gridBagConstraints0); + + panelToolbar.add( + new JLabel(Constant.messages.getString("params.toolbar.site.label")), + gridBagConstraints1); + panelToolbar.add(getSiteSelect(), gridBagConstraints2); + panelToolbar.add(getExportButton(), gridBagConstraints3); + + panelToolbar.add(t1, gridBagConstraintsx); + } + return panelToolbar; + } + + private TableExportButton getExportButton() { + if (exportButton == null) { + exportButton = new TableExportButton<>(getParamsTable()); + } + return exportButton; + } + + /* + * Displaying the ANTI CSRF options might not actually make that much sense... + private JButton getOptionsButton() { + if (optionsButton == null) { + optionsButton = new JButton(); + optionsButton.setToolTipText(Constant.messages.getString("params.toolbar.button.options")); + optionsButton.setIcon(new ImageIcon(ParamsPanel.class.getResource("/resource/icon/16/041.png"))); // 'Gears' icon + optionsButton.setEnabled(false); + optionsButton.addActionListener(new ActionListener () { + + @Override + public void actionPerformed(ActionEvent e) { + Control.getSingleton().getMenuToolsControl().options(Constant.messages.getString("options.acsrf.title")); + } + + }); + + } + return optionsButton; + } + */ + + private JScrollPane getJScrollPane() { + if (jScrollPane == null) { + jScrollPane = new JScrollPane(); + jScrollPane.setViewportView(getParamsTable()); + } + return jScrollPane; + } + + private void setParamsTableColumnSizes() { + + for (int i = 0; i < paramsTable.getColumnCount(); i++) { + DefaultTableColumnModel colModel = + (DefaultTableColumnModel) paramsTable.getColumnModel(); + TableColumn col = colModel.getColumn(i); + int width = 0; + + TableCellRenderer renderer = col.getHeaderRenderer(); + if (renderer == null) { + renderer = paramsTable.getTableHeader().getDefaultRenderer(); + } + Component comp = + renderer.getTableCellRendererComponent( + paramsTable, col.getHeaderValue(), false, false, 0, 0); + width = comp.getPreferredSize().width; + col.setPreferredWidth(width + 2); + } + paramsTable.getColumnModel().getColumn(6).setPreferredWidth(999); // value + } + + protected JXTable getParamsTable() { + if (paramsTable == null) { + paramsTable = new JXTable(paramsModel); + + paramsTable.setColumnSelectionAllowed(false); + paramsTable.setCellSelectionEnabled(false); + paramsTable.setRowSelectionAllowed(true); + paramsTable.setAutoCreateRowSorter(true); + paramsTable.setColumnControlVisible(true); + + this.setParamsTableColumnSizes(); + + paramsTable.setName(PANEL_NAME); + paramsTable.setDoubleBuffered(true); + paramsTable.addMouseListener( + new java.awt.event.MouseAdapter() { + @Override + public void mousePressed(java.awt.event.MouseEvent e) { + showPopupMenuIfTriggered(e); + } + + @Override + public void mouseReleased(java.awt.event.MouseEvent e) { + showPopupMenuIfTriggered(e); + } + + private void showPopupMenuIfTriggered(java.awt.event.MouseEvent e) { + if (e.isPopupTrigger()) { + + // Select table item + int row = paramsTable.rowAtPoint(e.getPoint()); + if (row < 0 + || !paramsTable.getSelectionModel().isSelectedIndex(row)) { + paramsTable.getSelectionModel().clearSelection(); + if (row >= 0) { + paramsTable + .getSelectionModel() + .setSelectionInterval(row, row); + } + } + + View.getSingleton() + .getPopupMenu() + .show(e.getComponent(), e.getX(), e.getY()); + } + } + }); + } + return paramsTable; + } + + private JComboBox getSiteSelect() { + if (siteSelect == null) { + siteSelect = new JComboBox<>(siteModel); + siteSelect.addItem(Constant.messages.getString("params.toolbar.site.select")); + siteSelect.setSelectedIndex(0); + + siteSelect.addActionListener( + new java.awt.event.ActionListener() { + + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + + String item = (String) siteSelect.getSelectedItem(); + if (item != null && siteSelect.getSelectedIndex() > 0) { + siteSelected(item); + } + } + }); + } + return siteSelect; + } + + public void addSite(String site) { + site = ScanPanel.cleanSiteName(site, true); + if (siteModel.getIndexOf(site) < 0) { + siteModel.addElement(site); + if (siteModel.getSize() == 2 && currentSite == null) { + // First site added, automatically select it + this.getSiteSelect().setSelectedIndex(1); + siteSelected(site); + } + } + } + + private void siteSelected(String site) { + site = ScanPanel.cleanSiteName(site, true); + if (!site.equals(currentSite)) { + siteModel.setSelectedItem(site); + + paramsModel = extension.getSiteParameters(site).getModel(); + this.getParamsTable().setModel(paramsModel); + + this.setParamsTableColumnSizes(); + + currentSite = site; + } + } + + public void nodeSelected(SiteNode node) { + if (node != null) { + siteSelected(ScanPanel.cleanSiteName(node, true)); + } + } + + public void reset() { + currentSite = null; + + siteModel.removeAllElements(); + siteSelect.addItem(Constant.messages.getString("params.toolbar.site.select")); + siteSelect.setSelectedIndex(0); + + paramsModel.removeAllElements(); + paramsModel.fireTableDataChanged(); + + paramsTable.setModel(paramsModel); + } + + /** + * Gets the current selected site. + * + * @return the current site + */ + public String getCurrentSite() { + return currentSite; + } + + protected HtmlParameterStats getSelectedParam() { + int selectedRow = this.getParamsTable().getSelectedRow(); + if (selectedRow == -1) { + return null; + } + + // TODO type is localized :( + String type = (String) this.getParamsTable().getValueAt(selectedRow, 0); + String name = (String) this.getParamsTable().getValueAt(selectedRow, 1); + + SiteParameters sps = extension.getSiteParameters(currentSite); + if (sps != null) { + return sps.getParam(HtmlParameter.Type.valueOf(type.toLowerCase()), name); // TODO HACK! + } + return null; + } + + /** + * Tells whether or not only one of the parameters is selected. + * + * @return {@code true} if only one parameter is selected, {@code false} otherwise. + * @see #getSelectedParam() + * @since 2.6.0 + */ + boolean isOnlyOneParamSelected() { + return getParamsTable().getSelectedRowCount() == 1; + } +} diff --git a/addOns/params/src/main/java/org/zaproxy/addon/params/ParamsTableModel.java b/addOns/params/src/main/java/org/zaproxy/addon/params/ParamsTableModel.java new file mode 100644 index 00000000000..c2efc6cd58c --- /dev/null +++ b/addOns/params/src/main/java/org/zaproxy/addon/params/ParamsTableModel.java @@ -0,0 +1,184 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2011 The ZAP Development Team + * + * 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.addon.params; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Vector; +import javax.swing.table.AbstractTableModel; +import org.parosproxy.paros.Constant; + +@SuppressWarnings("serial") +public class ParamsTableModel extends AbstractTableModel { + + private static final long serialVersionUID = 1L; + private static final int COLUMN_COUNT = 7; + + private final Vector columnNames; + private List paramStats; + + private int lastAddedRow; + private int lastEditedRow; + + public ParamsTableModel() { + super(); + columnNames = new Vector<>(COLUMN_COUNT); + columnNames.add(Constant.messages.getString("params.table.header.type")); + columnNames.add(Constant.messages.getString("params.table.header.name")); + columnNames.add(Constant.messages.getString("params.table.header.used")); + columnNames.add(Constant.messages.getString("params.table.header.numvals")); + columnNames.add(Constant.messages.getString("params.table.header.pcchange")); + columnNames.add(Constant.messages.getString("params.table.header.flags")); + columnNames.add(Constant.messages.getString("params.table.header.values")); + + paramStats = Collections.synchronizedList(new ArrayList<>()); + + lastAddedRow = -1; + lastEditedRow = -1; + } + + @Override + public int getColumnCount() { + return COLUMN_COUNT; + } + + @Override + public int getRowCount() { + return paramStats.size(); + } + + @Override + public String getColumnName(int col) { + return columnNames.get(col); + } + + public List getColumnNames() { + return new ArrayList<>(columnNames); + } + + @Override + public Object getValueAt(int row, int col) { + Object obj = null; + if (row >= paramStats.size()) { + return null; + } + // TODO this doesn't work if resorted?? + HtmlParameterStats param = paramStats.get(row); + switch (col) { + case 0: + obj = Constant.messages.getString("params.type." + param.getType().name()); + break; + case 1: + obj = param.getName(); + break; + case 2: + obj = param.getTimesUsed(); + break; + case 3: + obj = param.getValues().size(); + break; + case 4: + obj = getPercentChange(param); + break; + case 5: + obj = param.getAllFlags(); + break; + case 6: + obj = param.getValuesSummary(); + break; + } + return obj; + } + + private int getPercentChange(HtmlParameterStats param) { + if (param.getValues().size() == 1) { + return 0; + } + return (param.getValues().size() * 100 / param.getTimesUsed()); + } + + public HtmlParameterStats getHtmlParameterStatsAtRow(int row) { + // TODO this doesn't work if resorted?? + return paramStats.get(row); + } + + public synchronized void addHtmlParameterStats(HtmlParameterStats param) { + lastAddedRow = -1; + + for (int i = 0; i < paramStats.size(); i++) { + int cmp = param.compareTo(paramStats.get(i)); + if (cmp < 0) { + paramStats.add(i, param); + this.fireTableRowsInserted(i, i); + + lastAddedRow = i; + return; + } else if (cmp == 0) { + // Already matches, so ignore + lastAddedRow = i; + return; + } + } + + paramStats.add(param); + this.fireTableRowsInserted(paramStats.size() - 1, paramStats.size() - 1); + + lastAddedRow = paramStats.size() - 1; + } + + public int getLastAddedRow() { + return lastAddedRow; + } + + public int getLastEditedRow() { + return lastEditedRow; + } + + @Override + public boolean isCellEditable(int row, int col) { + return false; + } + + @Override + public Class getColumnClass(int c) { + switch (c) { + case 0: + return String.class; + case 1: + return String.class; + case 2: + return Integer.class; + case 3: + return Integer.class; + case 4: + return Integer.class; + case 5: + return String.class; + case 6: + return String.class; + } + return null; + } + + public void removeAllElements() { + paramStats.clear(); + } +} diff --git a/addOns/params/src/main/java/org/zaproxy/addon/params/PopupMenuAddAntiCSRF.java b/addOns/params/src/main/java/org/zaproxy/addon/params/PopupMenuAddAntiCSRF.java new file mode 100644 index 00000000000..e480b1d05c3 --- /dev/null +++ b/addOns/params/src/main/java/org/zaproxy/addon/params/PopupMenuAddAntiCSRF.java @@ -0,0 +1,75 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2011 The ZAP Development Team + * + * 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.addon.params; + +import java.awt.Component; +import org.parosproxy.paros.Constant; +import org.parosproxy.paros.extension.ExtensionPopupMenuItem; +import org.parosproxy.paros.network.HtmlParameter; + +@SuppressWarnings("serial") +public class PopupMenuAddAntiCSRF extends ExtensionPopupMenuItem { + + private static final long serialVersionUID = 1L; + + private ExtensionParams2 extension; + + public PopupMenuAddAntiCSRF() { + super(Constant.messages.getString("params.anticsrf.add.popup")); + this.addActionListener( + new java.awt.event.ActionListener() { + + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + + extension.addAntiCsrfToken(); + } + }); + } + + public void setExtension(ExtensionParams2 extension) { + this.extension = extension; + } + + @Override + public boolean isEnableForComponent(Component invoker) { + if (invoker.getName() != null && invoker.getName().equals(ParamsPanel.PANEL_NAME)) { + if (!extension.getParamsPanel().isOnlyOneParamSelected()) { + this.setEnabled(false); + return true; + } + + HtmlParameterStats item = extension.getParamsPanel().getSelectedParam(); + // Note that only form params are currently supported + if (item != null && !item.getFlags().contains(HtmlParameter.Flags.anticsrf.name())) { + this.setEnabled(true); + return true; + } + this.setEnabled(false); + return true; + } + return false; + } + + @Override + public boolean isSafe() { + return true; + } +} diff --git a/addOns/params/src/main/java/org/zaproxy/addon/params/PopupMenuAddSession.java b/addOns/params/src/main/java/org/zaproxy/addon/params/PopupMenuAddSession.java new file mode 100644 index 00000000000..39f1b097bc6 --- /dev/null +++ b/addOns/params/src/main/java/org/zaproxy/addon/params/PopupMenuAddSession.java @@ -0,0 +1,81 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2011 The ZAP Development Team + * + * 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.addon.params; + +import java.awt.Component; +import org.parosproxy.paros.Constant; +import org.parosproxy.paros.extension.ExtensionPopupMenuItem; +import org.parosproxy.paros.network.HtmlParameter; + +@SuppressWarnings("serial") +public class PopupMenuAddSession extends ExtensionPopupMenuItem { + + private static final long serialVersionUID = 1L; + + private ExtensionParams2 extension; + + public PopupMenuAddSession() { + super(Constant.messages.getString("params.session.add.popup")); + this.addActionListener( + new java.awt.event.ActionListener() { + + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + + extension.addSessionToken(); + } + }); + } + + public void setExtension(ExtensionParams2 extension) { + this.extension = extension; + } + + @Override + public boolean isEnableForComponent(Component invoker) { + if (invoker.getName() != null && invoker.getName().equals(ParamsPanel.PANEL_NAME)) { + if (!extension.getParamsPanel().isOnlyOneParamSelected()) { + this.setEnabled(false); + return true; + } + + HtmlParameterStats item = extension.getParamsPanel().getSelectedParam(); + // Note that only cookie params are currently supported + // TODO: Add support for URL tokens + if (item != null) { + if (HtmlParameter.Type.cookie.equals(item.getType())) { + if (!item.getFlags().contains(HtmlParameter.Flags.session.name())) { + this.setEnabled(true); + return true; + } + return false; + } + this.setEnabled(false); + return true; + } + } + return false; + } + + @Override + public boolean isSafe() { + return true; + } +} diff --git a/addOns/params/src/main/java/org/zaproxy/addon/params/PopupMenuParamSearch.java b/addOns/params/src/main/java/org/zaproxy/addon/params/PopupMenuParamSearch.java new file mode 100644 index 00000000000..4b7456da7a1 --- /dev/null +++ b/addOns/params/src/main/java/org/zaproxy/addon/params/PopupMenuParamSearch.java @@ -0,0 +1,62 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2011 The ZAP Development Team + * + * 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.addon.params; + +import java.awt.Component; +import org.parosproxy.paros.Constant; +import org.parosproxy.paros.extension.ExtensionPopupMenuItem; + +@SuppressWarnings("serial") +public class PopupMenuParamSearch extends ExtensionPopupMenuItem { + + private static final long serialVersionUID = 1L; + + private ExtensionParams2 extension; + + public PopupMenuParamSearch() { + super(Constant.messages.getString("params.search.popup")); + this.addActionListener( + new java.awt.event.ActionListener() { + + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + extension.searchForSelectedParam(); + } + }); + } + + public void setExtension(ExtensionParams2 extension) { + this.extension = extension; + } + + @Override + public boolean isEnableForComponent(Component invoker) { + if (invoker.getName() != null && invoker.getName().equals(ParamsPanel.PANEL_NAME)) { + this.setEnabled(extension.getParamsPanel().isOnlyOneParamSelected()); + return true; + } + return false; + } + + @Override + public boolean isSafe() { + return true; + } +} diff --git a/addOns/params/src/main/java/org/zaproxy/addon/params/PopupMenuRemoveAntiCSRF.java b/addOns/params/src/main/java/org/zaproxy/addon/params/PopupMenuRemoveAntiCSRF.java new file mode 100644 index 00000000000..8cac77887ea --- /dev/null +++ b/addOns/params/src/main/java/org/zaproxy/addon/params/PopupMenuRemoveAntiCSRF.java @@ -0,0 +1,71 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2011 The ZAP Development Team + * + * 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.addon.params; + +import java.awt.Component; +import org.parosproxy.paros.Constant; +import org.parosproxy.paros.extension.ExtensionPopupMenuItem; +import org.parosproxy.paros.network.HtmlParameter; + +@SuppressWarnings("serial") +public class PopupMenuRemoveAntiCSRF extends ExtensionPopupMenuItem { + + private static final long serialVersionUID = 1L; + + private ExtensionParams2 extension; + + public PopupMenuRemoveAntiCSRF() { + super(Constant.messages.getString("params.anticsrf.remove.popup")); + this.addActionListener( + new java.awt.event.ActionListener() { + + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + + extension.removeAntiCsrfToken(); + } + }); + } + + public void setExtension(ExtensionParams2 extension) { + this.extension = extension; + } + + @Override + public boolean isEnableForComponent(Component invoker) { + if (invoker.getName() != null && invoker.getName().equals(ParamsPanel.PANEL_NAME)) { + if (!extension.getParamsPanel().isOnlyOneParamSelected()) { + return false; + } + + HtmlParameterStats item = extension.getParamsPanel().getSelectedParam(); + if (item != null && item.getFlags().contains(HtmlParameter.Flags.anticsrf.name())) { + this.setEnabled(true); + return true; + } + } + return false; + } + + @Override + public boolean isSafe() { + return true; + } +} diff --git a/addOns/params/src/main/java/org/zaproxy/addon/params/PopupMenuRemoveSession.java b/addOns/params/src/main/java/org/zaproxy/addon/params/PopupMenuRemoveSession.java new file mode 100644 index 00000000000..85a5ec8b894 --- /dev/null +++ b/addOns/params/src/main/java/org/zaproxy/addon/params/PopupMenuRemoveSession.java @@ -0,0 +1,71 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2011 The ZAP Development Team + * + * 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.addon.params; + +import java.awt.Component; +import org.parosproxy.paros.Constant; +import org.parosproxy.paros.extension.ExtensionPopupMenuItem; +import org.parosproxy.paros.network.HtmlParameter; + +@SuppressWarnings("serial") +public class PopupMenuRemoveSession extends ExtensionPopupMenuItem { + + private static final long serialVersionUID = 1L; + + private ExtensionParams2 extension; + + public PopupMenuRemoveSession() { + super(Constant.messages.getString("params.session.remove.popup")); + this.addActionListener( + new java.awt.event.ActionListener() { + + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + + extension.removeSessionToken(); + } + }); + } + + public void setExtension(ExtensionParams2 extension) { + this.extension = extension; + } + + @Override + public boolean isEnableForComponent(Component invoker) { + if (invoker.getName() != null && invoker.getName().equals(ParamsPanel.PANEL_NAME)) { + if (!extension.getParamsPanel().isOnlyOneParamSelected()) { + return false; + } + + HtmlParameterStats item = extension.getParamsPanel().getSelectedParam(); + if (item != null && item.getFlags().contains(HtmlParameter.Flags.session.name())) { + this.setEnabled(true); + return true; + } + } + return false; + } + + @Override + public boolean isSafe() { + return true; + } +} diff --git a/addOns/params/src/main/java/org/zaproxy/addon/params/SiteParameters.java b/addOns/params/src/main/java/org/zaproxy/addon/params/SiteParameters.java new file mode 100644 index 00000000000..1c1c2b74f0c --- /dev/null +++ b/addOns/params/src/main/java/org/zaproxy/addon/params/SiteParameters.java @@ -0,0 +1,229 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2011 The ZAP Development Team + * + * 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.addon.params; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.parosproxy.paros.db.RecordParam; +import org.parosproxy.paros.network.HtmlParameter; +import org.parosproxy.paros.network.HtmlParameter.Type; +import org.parosproxy.paros.network.HttpMessage; +import org.zaproxy.zap.extension.httpsessions.ExtensionHttpSessions; +import org.zaproxy.zap.utils.ThreadUtils; + +public class SiteParameters { + private ExtensionParams2 extension; + private String site; + private ParamsTableModel model = new ParamsTableModel(); + private Map cookieParams = new HashMap<>(); + private Map urlParams = new HashMap<>(); + private Map formParams = new HashMap<>(); + private Map headerParams = new HashMap<>(); + private Map multipartParams = new HashMap<>(); + + public SiteParameters(ExtensionParams2 extension, String site) { + this.extension = extension; + this.site = site; + } + + public String getSite() { + return site; + } + + public void setSite(String site) { + this.site = site; + } + + /** + * Tells whether or not this site has any parameters (cookies, query, form parameters, or + * response header fields). + * + * @return {@code true} if this site has parameters, {@code false} otherwise. + * @since 2.5.0 + */ + public boolean hasParams() { + return !cookieParams.isEmpty() + || !urlParams.isEmpty() + || !formParams.isEmpty() + || !headerParams.isEmpty() + || !multipartParams.isEmpty(); + } + + public HtmlParameterStats getParam(HtmlParameter.Type type, String name) { + switch (type) { + case cookie: + return cookieParams.get(name); + case url: + return urlParams.get(name); + case form: + return formParams.get(name); + case header: + return headerParams.get(name); + case multipart: + return multipartParams.get(name); + } + return null; + } + + public List getParams(HtmlParameter.Type type) { + List params = new ArrayList<>(); + switch (type) { + case cookie: + params.addAll(this.cookieParams.values()); + break; + case url: + params.addAll(this.urlParams.values()); + break; + case form: + params.addAll(this.formParams.values()); + break; + case header: + params.addAll(this.headerParams.values()); + break; + case multipart: + params.addAll(this.multipartParams.values()); + break; + } + return params; + } + + public List getParams() { + List params = new ArrayList<>(); + params.addAll(this.cookieParams.values()); + params.addAll(this.urlParams.values()); + params.addAll(this.formParams.values()); + params.addAll(this.headerParams.values()); + params.addAll(this.multipartParams.values()); + return params; + } + + public synchronized HtmlParameterStats addParam( + String site, HtmlParameter param, HttpMessage msg) { + Map params = null; + HtmlParameterStats p; + + switch (param.getType()) { + case cookie: + params = cookieParams; + break; + case url: + params = urlParams; + break; + case form: + params = formParams; + break; + case header: + params = headerParams; + break; + case multipart: + params = multipartParams; + break; + } + + if (params != null && params.containsKey(param.getName())) { + p = params.get(param.getName()); + p.incTimesUsed(); + p.addValue(param.getValue()); + } else { + // It's a new parameter + p = + new HtmlParameterStats( + site, + param.getName(), + param.getType(), + param.getValue(), + param.getFlags()); + + // If the HttpSessions extension is active, check if the token is a session token and, + // if it is, mark it so + ExtensionHttpSessions extSession = extension.getExtensionHttpSessions(); + if (extSession != null) { + if (param.getType().equals(Type.cookie) + && extSession.isSessionToken(site, param.getName())) { + // Only Cookies can be session params + // TODO: Add support for URL tokens + p.addFlag(HtmlParameter.Flags.session.name()); + } + } + + if (params == null) { + params = new HashMap<>(); + } + params.put(param.getName(), p); + ThreadUtils.invokeLater(() -> model.addHtmlParameterStats(p)); + } + return p; + } + + public ParamsTableModel getModel() { + return model; + } + + private Set stringToSet(String str) { + Set set = new HashSet<>(); + // TODO handle encoded commas? + String[] array = str.split(","); + for (String s : array) { + set.add(s); + } + return set; + } + + public synchronized void addParam(String site2, RecordParam param) { + Map params = null; + + HtmlParameter.Type type = HtmlParameter.Type.valueOf(param.getType()); + switch (type) { + case cookie: + params = cookieParams; + break; + case url: + params = urlParams; + break; + case form: + params = formParams; + break; + case header: + params = headerParams; + break; + case multipart: + params = multipartParams; + break; + } + // These should all be new + HtmlParameterStats p = + new HtmlParameterStats( + param.getParamId(), + site, + param.getName(), + param.getType(), + param.getUsed(), + stringToSet(param.getValues()), + stringToSet(param.getFlags())); + if (params != null) { + params.put(param.getName(), p); + ThreadUtils.invokeLater(() -> model.addHtmlParameterStats(p)); + } + } +} diff --git a/addOns/params/src/main/javahelp/org/zaproxy/addon/parans/resources/help/contents/params.html b/addOns/params/src/main/javahelp/org/zaproxy/addon/parans/resources/help/contents/params.html new file mode 100644 index 00000000000..197824bd991 --- /dev/null +++ b/addOns/params/src/main/javahelp/org/zaproxy/addon/parans/resources/help/contents/params.html @@ -0,0 +1,56 @@ + + + + + +Params tab + + + +

Params tab

+ +

This shows a summary of the parameters and response header fields a site uses.

+Sites can be selected via the toolbar or the Sites tab.

+For each parameter you can see: + + + + + + + + + + + + + + + + +
    The type - Cookie, FORM, URL, or Header
    The name of the parameter (or response header)
    The number of times it has been used
    The number of unique values
    The percentage change, where 0 means only one value has been used and 100 means all values are unique
    The flags - including cookie flags and anticsrf and session
    Some of the values - the full set of values may not all be visible
+ +

Right click menu

+Right clicking on a node will bring up a menu which will allow you to: + +

Search

+This will show all examples of the parameter selected in the Search tab. + +

Flag as Anti CSRF token

+This will flag the parameter as an Anti CSRF token. + +

Unflag as Anti CSRF token

+This will remove the Anti CSRF token flag from the parameter.
+ +

Flag as Session token

+ This will mark the parameter as a Session token for the current Site and will notify the + HTTP Sessions tool accordingly. +
+ +

Unflag as Session token

+This will unmark the parameter as a Session token for the current site and will notify the + HTTP Sessions tool accordingly. +
+ + + diff --git a/addOns/params/src/main/javahelp/org/zaproxy/addon/parans/resources/help/helpset.hs b/addOns/params/src/main/javahelp/org/zaproxy/addon/parans/resources/help/helpset.hs new file mode 100644 index 00000000000..84d5bdf0e9e --- /dev/null +++ b/addOns/params/src/main/javahelp/org/zaproxy/addon/parans/resources/help/helpset.hs @@ -0,0 +1,41 @@ + + + + Spider Add-On + + + params + + + + + TOC + + org.zaproxy.zap.extension.help.ZapTocView + toc.xml + + + + Index + + javax.help.IndexView + index.xml + + + + Search + + javax.help.SearchView + + JavaHelpSearch + + + + + Favorites + + javax.help.FavoritesView + + diff --git a/addOns/params/src/main/javahelp/org/zaproxy/addon/parans/resources/help/index.xml b/addOns/params/src/main/javahelp/org/zaproxy/addon/parans/resources/help/index.xml new file mode 100644 index 00000000000..b6f1f37f947 --- /dev/null +++ b/addOns/params/src/main/javahelp/org/zaproxy/addon/parans/resources/help/index.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/addOns/params/src/main/javahelp/org/zaproxy/addon/parans/resources/help/map.jhm b/addOns/params/src/main/javahelp/org/zaproxy/addon/parans/resources/help/map.jhm new file mode 100644 index 00000000000..62f2d47fbaf --- /dev/null +++ b/addOns/params/src/main/javahelp/org/zaproxy/addon/parans/resources/help/map.jhm @@ -0,0 +1,8 @@ + + + + + + diff --git a/addOns/params/src/main/javahelp/org/zaproxy/addon/parans/resources/help/toc.xml b/addOns/params/src/main/javahelp/org/zaproxy/addon/parans/resources/help/toc.xml new file mode 100644 index 00000000000..614e154ff7a --- /dev/null +++ b/addOns/params/src/main/javahelp/org/zaproxy/addon/parans/resources/help/toc.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/addOns/params/src/main/resources/org/zaproxy/addon/params/resources/Messages.properties b/addOns/params/src/main/resources/org/zaproxy/addon/params/resources/Messages.properties new file mode 100644 index 00000000000..a52f37218d1 --- /dev/null +++ b/addOns/params/src/main/resources/org/zaproxy/addon/params/resources/Messages.properties @@ -0,0 +1,26 @@ +params.anticsrf.add.popup = Add Name as Anti-CSRF Token +params.anticsrf.remove.popup = Remove Name as Anti-CSRF Token +params.api.desc = +params.api.view.params = Shows the parameters for the specified site, or for all sites if the site is not specified +params.api.view.params.param.site = +params.desc = Summarise and analyse FORM and URL parameters as well as cookies and headers +params.name = Parameters Add-on +params.panel.mnemonic = p +params.panel.title = Params +params.search.popup = Search +params.session.add.popup = Flag as Session Token +params.session.remove.popup = Unflag as Session Token +params.table.header.flags = Flags +params.table.header.name = Name +params.table.header.numvals = # Values +params.table.header.pcchange = % Change +params.table.header.type = Type +params.table.header.used = Used +params.table.header.values = Values +params.toolbar.site.label = Site: +params.toolbar.site.select = --Select Site-- +params.type.cookie = Cookie +params.type.form = FORM +params.type.header = Header +params.type.multipart = Multipart +params.type.url = URL diff --git a/addOns/params/src/test/java/org/zaproxy/addon/params/ExtensionParams2UnitTest.java b/addOns/params/src/test/java/org/zaproxy/addon/params/ExtensionParams2UnitTest.java new file mode 100644 index 00000000000..b66ce1c045b --- /dev/null +++ b/addOns/params/src/test/java/org/zaproxy/addon/params/ExtensionParams2UnitTest.java @@ -0,0 +1,75 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2022 The ZAP Development Team + * + * 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.addon.params; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.withSettings; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.quality.Strictness; +import org.parosproxy.paros.control.Control; +import org.parosproxy.paros.extension.ExtensionHook; +import org.parosproxy.paros.extension.ExtensionLoader; +import org.parosproxy.paros.extension.SessionChangedListener; +import org.parosproxy.paros.model.Model; +import org.parosproxy.paros.model.Session; +import org.zaproxy.addon.pscan.ExtensionPassiveScan2; +import org.zaproxy.addon.pscan.PassiveScannersManager; + +/** Unit test for {@link ExtensionSParams2}. */ +class ExtensionParams2UnitTest { + + private ExtensionParams2 extension; + + @BeforeEach + void setUp() { + extension = new ExtensionParams2(); + } + + @Test + void shouldAddSessionChangedListenerOnHook() { + // Given + Model model = mock(Model.class, withSettings().strictness(Strictness.LENIENT)); + Session session = mock(Session.class, withSettings().strictness(Strictness.LENIENT)); + given(model.getSession()).willReturn(session); + ExtensionLoader extensionLoader = + mock(ExtensionLoader.class, withSettings().strictness(Strictness.LENIENT)); + Control.initSingletonForTesting(model, extensionLoader); + ExtensionPassiveScan2 extPscan = + mock(ExtensionPassiveScan2.class, withSettings().strictness(Strictness.LENIENT)); + given(extensionLoader.getExtension(ExtensionPassiveScan2.class)).willReturn(extPscan); + given(extPscan.getPassiveScannersManager()).willReturn(mock(PassiveScannersManager.class)); + ExtensionHook extensionHook = mock(ExtensionHook.class); + // When + extension.hook(extensionHook); + // Then + ArgumentCaptor argument = + ArgumentCaptor.forClass(SessionChangedListener.class); + verify(extensionHook).addSessionListener(argument.capture()); + assertThat(argument.getValue(), is(notNullValue())); + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 177cde66ebf..51b0664f385 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -69,6 +69,7 @@ var addOns = "packpentester", "packscanrules", "paramdigger", + "params", "plugnhack", "postman", "pscan",