diff --git a/addOns/grpc/CHANGELOG.md b/addOns/grpc/CHANGELOG.md index 50c1cb9429..f0acef79d8 100644 --- a/addOns/grpc/CHANGELOG.md +++ b/addOns/grpc/CHANGELOG.md @@ -4,6 +4,10 @@ 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/). ## Unreleased + +### Added +- gRPC WebSocket Support Added + ### Fixed - Do not try to decode non-gRPC responses when active scanning, which would lead to unnecessary warnings. diff --git a/addOns/grpc/grpc.gradle.kts b/addOns/grpc/grpc.gradle.kts index 72e51d8f24..6a9a2cc594 100644 --- a/addOns/grpc/grpc.gradle.kts +++ b/addOns/grpc/grpc.gradle.kts @@ -6,6 +6,18 @@ zapAddOn { manifest { author.set("ZAP Dev Team") url.set("https://www.zaproxy.org/docs/desktop/addons/grpc-support/") + extensions { + register("org.zaproxy.addon.grpc.internal.websocket.ExtensionGrpcWebSocket") { + classnames { + allowed.set(listOf("org.zaproxy.addon.grpc.internal.websocket")) + } + dependencies { + addOns { + register("websocket") + } + } + } + } } } @@ -18,6 +30,7 @@ crowdin { } dependencies { + zapAddOn("websocket") testImplementation(project(":testutils")) implementation("io.grpc:grpc-protobuf:1.61.1") diff --git a/addOns/grpc/src/main/java/org/zaproxy/addon/grpc/ExtensionGrpc.java b/addOns/grpc/src/main/java/org/zaproxy/addon/grpc/ExtensionGrpc.java index 012344c024..04e4fd7430 100644 --- a/addOns/grpc/src/main/java/org/zaproxy/addon/grpc/ExtensionGrpc.java +++ b/addOns/grpc/src/main/java/org/zaproxy/addon/grpc/ExtensionGrpc.java @@ -22,6 +22,7 @@ import org.parosproxy.paros.Constant; import org.parosproxy.paros.extension.ExtensionAdaptor; import org.parosproxy.paros.extension.ExtensionHook; +import org.zaproxy.addon.grpc.internal.DecoderUtils; import org.zaproxy.addon.grpc.internal.HttpPanelGrpcView; import org.zaproxy.addon.grpc.internal.VariantGrpc; import org.zaproxy.zap.extension.httppanel.component.split.request.RequestSplitComponent; @@ -98,7 +99,9 @@ public String getName() { @Override public HttpPanelView getNewView() { - return new HttpPanelGrpcView(new ResponseBodyByteHttpPanelViewModel()); + return new HttpPanelGrpcView( + new ResponseBodyByteHttpPanelViewModel(), + DecoderUtils.DecodingMethod.BASE64_ENCODED); } @Override @@ -118,7 +121,9 @@ public String getName() { @Override public HttpPanelView getNewView() { - return new HttpPanelGrpcView(new RequestBodyByteHttpPanelViewModel()); + return new HttpPanelGrpcView( + new RequestBodyByteHttpPanelViewModel(), + DecoderUtils.DecodingMethod.BASE64_ENCODED); } @Override diff --git a/addOns/grpc/src/main/java/org/zaproxy/addon/grpc/internal/DecoderUtils.java b/addOns/grpc/src/main/java/org/zaproxy/addon/grpc/internal/DecoderUtils.java index cdc6ef02c6..68f243dd8a 100644 --- a/addOns/grpc/src/main/java/org/zaproxy/addon/grpc/internal/DecoderUtils.java +++ b/addOns/grpc/src/main/java/org/zaproxy/addon/grpc/internal/DecoderUtils.java @@ -44,6 +44,11 @@ public class DecoderUtils { public static final int LENGTH_DELIMITED_WIRE_TYPE = 2; public static final int BIT32_WIRE_TYPE = 5; + public enum DecodingMethod { + BASE64_ENCODED, + DIRECT + } + static boolean isGraphic(byte ch) { // Check if the character is printable // Printable characters have unicode values greater than 32 (excluding control diff --git a/addOns/grpc/src/main/java/org/zaproxy/addon/grpc/internal/HttpPanelGrpcView.java b/addOns/grpc/src/main/java/org/zaproxy/addon/grpc/internal/HttpPanelGrpcView.java index a6d87deda5..d571059ead 100644 --- a/addOns/grpc/src/main/java/org/zaproxy/addon/grpc/internal/HttpPanelGrpcView.java +++ b/addOns/grpc/src/main/java/org/zaproxy/addon/grpc/internal/HttpPanelGrpcView.java @@ -50,7 +50,10 @@ public class HttpPanelGrpcView implements HttpPanelView, HttpPanelViewModelListe private ProtoBufMessageEncoder protoBufMessageEncoder; private AbstractByteHttpPanelViewModel model; - public HttpPanelGrpcView(AbstractByteHttpPanelViewModel model) { + private final DecoderUtils.DecodingMethod decodingMethod; + + public HttpPanelGrpcView( + AbstractByteHttpPanelViewModel model, DecoderUtils.DecodingMethod decodingMethod) { httpPanelGrpcArea = new HttpPanelGrpcArea(); RTextScrollPane scrollPane = new RTextScrollPane(httpPanelGrpcArea); scrollPane.setLineNumbersEnabled(false); @@ -60,6 +63,7 @@ public HttpPanelGrpcView(AbstractByteHttpPanelViewModel model) { model.addHttpPanelViewModelListener(this); protoBufMessageDecoder = new ProtoBufMessageDecoder(); protoBufMessageEncoder = new ProtoBufMessageEncoder(); + this.decodingMethod = decodingMethod; } @Override @@ -126,7 +130,11 @@ public void save() { try { protoBufMessageEncoder.encode(EncoderUtils.parseIntoList(text)); byte[] encodedMessage = protoBufMessageEncoder.getOutputEncodedMessage(); - this.model.setData(Base64.getEncoder().encode(encodedMessage)); + if (decodingMethod == DecoderUtils.DecodingMethod.BASE64_ENCODED) { + this.model.setData(Base64.getEncoder().encode(encodedMessage)); + } else { + this.model.setData(encodedMessage); + } } catch (Exception e) { showInvalidMessageFormatError(e.getMessage()); } @@ -147,8 +155,13 @@ public void dataChanged(HttpPanelViewModelEvent e) { httpPanelGrpcArea.setBorder(null); try { body = DecoderUtils.splitMessageBodyAndStatusCode(body); - body = Base64.getDecoder().decode(body); - byte[] payload = DecoderUtils.extractPayload(body); + byte[] payload; + if (decodingMethod == DecoderUtils.DecodingMethod.BASE64_ENCODED) { + body = Base64.getDecoder().decode(body); + payload = DecoderUtils.extractPayload(body); + } else { + payload = body; + } if (payload.length == 0) { httpPanelGrpcArea.setText(""); } else { diff --git a/addOns/grpc/src/main/java/org/zaproxy/addon/grpc/internal/websocket/ExtensionGrpcWebSocket.java b/addOns/grpc/src/main/java/org/zaproxy/addon/grpc/internal/websocket/ExtensionGrpcWebSocket.java new file mode 100644 index 0000000000..0f1a4afbbb --- /dev/null +++ b/addOns/grpc/src/main/java/org/zaproxy/addon/grpc/internal/websocket/ExtensionGrpcWebSocket.java @@ -0,0 +1,102 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 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.grpc.internal.websocket; + +import java.util.List; +import org.parosproxy.paros.Constant; +import org.parosproxy.paros.extension.Extension; +import org.parosproxy.paros.extension.ExtensionAdaptor; +import org.parosproxy.paros.extension.ExtensionHook; +import org.zaproxy.addon.grpc.ExtensionGrpc; +import org.zaproxy.addon.grpc.internal.DecoderUtils; +import org.zaproxy.addon.grpc.internal.HttpPanelGrpcView; +import org.zaproxy.zap.extension.httppanel.view.HttpPanelView; +import org.zaproxy.zap.extension.websocket.ExtensionWebSocket; +import org.zaproxy.zap.extension.websocket.ui.httppanel.component.WebSocketComponent; +import org.zaproxy.zap.extension.websocket.ui.httppanel.models.ByteWebSocketPanelViewModel; +import org.zaproxy.zap.view.HttpPanelManager; + +public class ExtensionGrpcWebSocket extends ExtensionAdaptor { + + public static final String NAME = "ExtensionGrpcWebSocket"; + private static final List> DEPENDENCIES = + List.of(ExtensionGrpc.class, ExtensionWebSocket.class); + + public ExtensionGrpcWebSocket() { + super(NAME); + } + + @Override + public List> getDependencies() { + return DEPENDENCIES; + } + + @Override + public void hook(ExtensionHook extensionHook) { + if (hasView()) { + HttpPanelManager manager = HttpPanelManager.getInstance(); + manager.addRequestViewFactory(WebSocketComponent.NAME, new WebSocketGrpcViewFactory()); + manager.addResponseViewFactory(WebSocketComponent.NAME, new WebSocketGrpcViewFactory()); + } + } + + @Override + public boolean canUnload() { + return true; + } + + @Override + public void unload() { + HttpPanelManager manager = HttpPanelManager.getInstance(); + manager.removeRequestViewFactory(WebSocketComponent.NAME, WebSocketGrpcViewFactory.NAME); + manager.removeResponseViewFactory(WebSocketComponent.NAME, WebSocketGrpcViewFactory.NAME); + } + + @Override + public String getUIName() { + return Constant.messages.getString("grpc.websocket.name"); + } + + @Override + public String getDescription() { + return Constant.messages.getString("grpc.websocket.desc"); + } + + private static final class WebSocketGrpcViewFactory + implements HttpPanelManager.HttpPanelViewFactory { + public static final String NAME = "WebSocketGrpcViewFactory"; + + @Override + public String getName() { + return NAME; + } + + @Override + public HttpPanelView getNewView() { + return new HttpPanelGrpcView( + new ByteWebSocketPanelViewModel(), DecoderUtils.DecodingMethod.DIRECT); + } + + @Override + public Object getOptions() { + return null; + } + } +} diff --git a/addOns/grpc/src/main/javahelp/org/zaproxy/addon/grpc/resources/help/contents/grpc.html b/addOns/grpc/src/main/javahelp/org/zaproxy/addon/grpc/resources/help/contents/grpc.html index 4e1290122b..4e7aca8441 100644 --- a/addOns/grpc/src/main/javahelp/org/zaproxy/addon/grpc/resources/help/contents/grpc.html +++ b/addOns/grpc/src/main/javahelp/org/zaproxy/addon/grpc/resources/help/contents/grpc.html @@ -60,6 +60,11 @@

See also

gRPC Variant for information about the gRPC variant. + +      + gRPC WebSocket + for information about the gRPC WebSocket Support. + diff --git a/addOns/grpc/src/main/javahelp/org/zaproxy/addon/grpc/resources/help/contents/variant.html b/addOns/grpc/src/main/javahelp/org/zaproxy/addon/grpc/resources/help/contents/variant.html index 5f271ad081..d501406bc3 100644 --- a/addOns/grpc/src/main/javahelp/org/zaproxy/addon/grpc/resources/help/contents/variant.html +++ b/addOns/grpc/src/main/javahelp/org/zaproxy/addon/grpc/resources/help/contents/variant.html @@ -18,6 +18,11 @@

See also

gRPC for an overview of the gRPC add-on. + +      + gRPC WebSocket + for information about the gRPC WebSocket Support. + diff --git a/addOns/grpc/src/main/javahelp/org/zaproxy/addon/grpc/resources/help/contents/websocket.html b/addOns/grpc/src/main/javahelp/org/zaproxy/addon/grpc/resources/help/contents/websocket.html new file mode 100644 index 0000000000..79e869aaa4 --- /dev/null +++ b/addOns/grpc/src/main/javahelp/org/zaproxy/addon/grpc/resources/help/contents/websocket.html @@ -0,0 +1,27 @@ + + + + + gRPC WebSocket + + +

gRPC WebSocket Support

+ +The gRPC WebSocket support feature enables the WebSocket add-on to decode and encode gRPC messages seamlessly. This integration allows users to inspect, modify, and manage gRPC communication over WebSockets. +

See also

+ + + + + + + + + + + + +
    gRPCfor an overview of the gRPC add-on.
    gRPC Variantfor information about the gRPC variant.
+ + + diff --git a/addOns/grpc/src/main/javahelp/org/zaproxy/addon/grpc/resources/help/index.xml b/addOns/grpc/src/main/javahelp/org/zaproxy/addon/grpc/resources/help/index.xml index b6ad931df0..1cc510bc92 100644 --- a/addOns/grpc/src/main/javahelp/org/zaproxy/addon/grpc/resources/help/index.xml +++ b/addOns/grpc/src/main/javahelp/org/zaproxy/addon/grpc/resources/help/index.xml @@ -6,4 +6,5 @@ + \ No newline at end of file diff --git a/addOns/grpc/src/main/javahelp/org/zaproxy/addon/grpc/resources/help/map.jhm b/addOns/grpc/src/main/javahelp/org/zaproxy/addon/grpc/resources/help/map.jhm index fb2248148e..31bf2f4659 100644 --- a/addOns/grpc/src/main/javahelp/org/zaproxy/addon/grpc/resources/help/map.jhm +++ b/addOns/grpc/src/main/javahelp/org/zaproxy/addon/grpc/resources/help/map.jhm @@ -7,4 +7,5 @@ + \ No newline at end of file diff --git a/addOns/grpc/src/main/javahelp/org/zaproxy/addon/grpc/resources/help/toc.xml b/addOns/grpc/src/main/javahelp/org/zaproxy/addon/grpc/resources/help/toc.xml index 71ae3a32ec..7877a475cb 100644 --- a/addOns/grpc/src/main/javahelp/org/zaproxy/addon/grpc/resources/help/toc.xml +++ b/addOns/grpc/src/main/javahelp/org/zaproxy/addon/grpc/resources/help/toc.xml @@ -8,6 +8,7 @@ + diff --git a/addOns/grpc/src/main/resources/org/zaproxy/addon/grpc/resources/Messages.properties b/addOns/grpc/src/main/resources/org/zaproxy/addon/grpc/resources/Messages.properties index 25506c98d4..cc2a66af17 100644 --- a/addOns/grpc/src/main/resources/org/zaproxy/addon/grpc/resources/Messages.properties +++ b/addOns/grpc/src/main/resources/org/zaproxy/addon/grpc/resources/Messages.properties @@ -10,3 +10,5 @@ grpc.encoder.nested.message.missing.braces.error = Invalid Format: Extra or miss grpc.encoder.nested.message.newline.error = Invalid Format: Nested message must contain a newline immediately after every curly brace grpc.name = gRPC Support grpc.panel.view.name = gRPC +grpc.websocket.desc = Provides the WebSocket gRPC view and edit features +grpc.websocket.name = gRPC Websocket Support