Skip to content

Commit

Permalink
Client: Map initial context menu options
Browse files Browse the repository at this point in the history
Signed-off-by: Simon Bennetts <psiinon@gmail.com>
  • Loading branch information
psiinon committed Oct 31, 2023
1 parent f9ba1a5 commit b23e746
Show file tree
Hide file tree
Showing 16 changed files with 571 additions and 2 deletions.
1 change: 1 addition & 0 deletions addOns/client/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## Unreleased
### Added
- Note about custom containers in the help.
- Client Map context menu items.

### Changed
- Updated the Chrome extension to v0.0.7.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ public void setClientNode(ClientNode node) {
this.setTabFocus();
}

public String getCurrentUrl() {
return this.nodeDetailsPanel.getCurrentUrl();
}

public void clear() {
this.nodeDetailsPanel.clear();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package org.zaproxy.addon.client;

import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
Expand Down Expand Up @@ -115,6 +116,14 @@ public synchronized ClientNode getOrAddNode(String url, boolean visited, boolean
return child;
}

public void deleteNodes(List<ClientNode> nodes) {
for (ClientNode node : nodes) {
if (!node.isRoot()) {
removeNodeFromParent(node);
}
}
}

public void clear() {
root.removeAllChildren();
this.nodeStructureChanged(root);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,31 @@
*/
package org.zaproxy.addon.client;

import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.swing.ImageIcon;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.tree.TreePath;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.extension.AbstractPanel;
import org.parosproxy.paros.view.View;
import org.zaproxy.zap.view.LayoutHelper;

@SuppressWarnings("serial")
public class ClientMapPanel extends AbstractPanel {

public static final String CLIENT_TREE_NAME = "treeClient";

private static final long serialVersionUID = 1L;
private ExtensionClientIntegration extension;
private JTree clientTree;
private ClientMap clientMap;
private JScrollPane scrollPane;

Expand All @@ -59,11 +69,12 @@ public ClientMapPanel(ExtensionClientIntegration extension, ClientMap clientMap)
}

public void setClientMap(ClientMap clientMap) {
JTree clientTree = new JTree(clientMap);
clientTree = new JTree(clientMap);
clientTree.setShowsRootHandles(true);
clientTree.setName("treeClient");
clientTree.setName(CLIENT_TREE_NAME);
clientTree.setToggleClickCount(1);
clientTree.setCellRenderer(new ClientMapTreeCellRenderer());
clientTree.setComponentPopupMenu(new ClientCustomPopupMenu());

clientTree.addTreeSelectionListener(
e -> {
Expand All @@ -81,4 +92,55 @@ public void clear() {
this.clientMap.clear();
}
}

public List<ClientNode> getSelectedNodes() {
return Stream.ofNullable(clientTree.getSelectionPaths())
.flatMap(Stream::of)
.map(TreePath::getLastPathComponent)
.map(ClientNode.class::cast)
.collect(Collectors.toList());
}

public void deleteNodes(List<ClientNode> nodes) {
for (ClientNode node : nodes) {
if (!node.isRoot()) {
clientMap.removeNodeFromParent(node);
}
}
}

public ClientNode getSelectedNode() {
return (ClientNode) clientTree.getSelectionPath().getLastPathComponent();
}

public ExtensionClientIntegration getExtension() {
return extension;
}

protected class ClientCustomPopupMenu extends JPopupMenu {
private static final long serialVersionUID = 1L;

@Override
public void show(Component invoker, int x, int y) {
TreePath tp = clientTree.getPathForLocation(x, y);
if (tp != null) {
boolean select = true;
// Only select a new item if the current item is not
// already selected - this is to allow multiple items
// to be selected
if (clientTree.getSelectionPaths() != null) {
for (TreePath t : clientTree.getSelectionPaths()) {
if (t.equals(tp)) {
select = false;
break;
}
}
}
if (select) {
clientTree.getSelectionModel().setSelectionPath(tp);
}
}
View.getSingleton().getPopupMenu().show(invoker, x, y);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,9 @@ public String getSite() {
}
return parent.getSite();
}

@Override
public String toString() {
return getUserObject().getUrl();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ public void setClientNode(ClientNode node) {
.setComponents(new ArrayList<>(node.getUserObject().getComponents()));
}

public String getCurrentUrl() {
return this.urlLabel.getText();
}

public void clear() {
this.urlLabel.setText("");
this.getComponentTableModel().setComponents(new ArrayList<>());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.nio.file.StandardOpenOption;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.parosproxy.paros.Constant;
Expand All @@ -39,6 +40,7 @@
import org.parosproxy.paros.extension.SessionChangedListener;
import org.parosproxy.paros.extension.history.ExtensionHistory;
import org.parosproxy.paros.model.Session;
import org.parosproxy.paros.view.View;
import org.zaproxy.addon.client.impl.ClientZestRecorder;
import org.zaproxy.addon.network.ExtensionNetwork;
import org.zaproxy.zap.ZAP;
Expand Down Expand Up @@ -106,6 +108,21 @@ public void hook(ExtensionHook extensionHook) {
extensionHook.getHookView().addSelectPanel(getClientMapPanel());
extensionHook.getHookView().addWorkPanel(getClientDetailsPanel());
extensionHook.getHookView().addStatusPanel(getClientHistoryPanel());
extensionHook
.getHookMenu()
.addPopupMenuItem(new PopupMenuClientAttack(this.getClientMapPanel()));
extensionHook
.getHookMenu()
.addPopupMenuItem(new PopupMenuClientCopyUrls(this.getClientMapPanel()));
extensionHook
.getHookMenu()
.addPopupMenuItem(new PopupMenuClientDelete(this.getClientMapPanel()));
extensionHook
.getHookMenu()
.addPopupMenuItem(new PopupMenuClientOpenInBrowser(clientMapPanel));
extensionHook
.getHookMenu()
.addPopupMenuItem(new PopupMenuClientShowInSites(this.getClientMapPanel()));
}
}

Expand Down Expand Up @@ -255,6 +272,18 @@ public void clientNodeChanged(ClientNode node) {
this.clientTree.nodeChanged(node);
}

public void deleteNodes(List<ClientNode> nodes) {
this.clientTree.deleteNodes(nodes);
if (View.isInitialised()) {
String displayedUrl = this.getClientDetailsPanel().getCurrentUrl();
if (StringUtils.isNotBlank(displayedUrl)
&& nodes.stream()
.anyMatch(n -> displayedUrl.equals(n.getUserObject().getUrl()))) {
this.getClientDetailsPanel().clear();
}
}
}

private ClientMapPanel getClientMapPanel() {
if (clientMapPanel == null) {
clientMapPanel = new ClientMapPanel(this, clientTree);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright 2023 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.client;

import java.awt.Component;
import java.awt.event.ActionEvent;
import org.parosproxy.paros.Constant;

public class PopupMenuClientAttack extends PopupMenuItemClient {

private static final long serialVersionUID = 1L;

public PopupMenuClientAttack(ClientMapPanel clientMapPanel) {
super(Constant.messages.getString("client.tree.popup.attack"), clientMapPanel);
}

@Override
public boolean isEnableForComponent(Component invoker) {
boolean enabled = super.isEnableForComponent(invoker);
if (enabled) {
// For now its a placeholder / tease ;)
this.setEnabled(false);
}
return enabled;
}

@Override
public void performAction(ActionEvent e) {
// Do nothing for now
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright 2023 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.client;

import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import org.parosproxy.paros.Constant;

public class PopupMenuClientCopyUrls extends PopupMenuItemClient {

private static final long serialVersionUID = 1L;

public PopupMenuClientCopyUrls(ClientMapPanel clientMapPanel) {
super(Constant.messages.getString("client.tree.popup.copyurls"), clientMapPanel);
}

@Override
public void performAction(ActionEvent e) {
StringBuilder sb = new StringBuilder();
for (ClientNode node : getClientMapPanel().getSelectedNodes()) {
if (!node.isRoot()
&& node.getUserObject() != null
&& !node.getUserObject().isStorage()) {
sb.append(node.getUserObject().getUrl());
sb.append('\n');
}
}
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents(new StringSelection(sb.toString()), null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright 2023 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.client;

import java.awt.event.ActionEvent;
import javax.swing.JOptionPane;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.view.View;

public class PopupMenuClientDelete extends PopupMenuItemClient {

private static final long serialVersionUID = 1L;

public PopupMenuClientDelete(ClientMapPanel clientMapPanel) {
super(Constant.messages.getString("client.tree.popup.delete"), clientMapPanel);
}

@Override
public void performAction(ActionEvent e) {
if (View.getSingleton()
.showConfirmDialog(
Constant.messages.getString("client.tree.popup.delete.confirm"))
== JOptionPane.OK_OPTION) {
getClientMapPanel().getExtension().deleteNodes(getClientMapPanel().getSelectedNodes());
}
}
}
Loading

0 comments on commit b23e746

Please sign in to comment.