Skip to content

Commit

Permalink
Merge pull request #4846 from thc202/network/proxy-modified-opt
Browse files Browse the repository at this point in the history
network: add optional legacy core behaviour
  • Loading branch information
psiinon authored Aug 31, 2023
2 parents ec24a8b + b576d0b commit a616259
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ public class ConnectionOptions extends VersionedAbstractParam {
private static final String DNS_TTL_SUCCESSFUL_QUERIES_SECURITY_PROPERTY =
"networkaddress.cache.ttl";

private boolean legacyRemoveCacheHeaders;

private static final boolean DEFAULT_STORE_HTTP_PROXY_PASS = true;

private List<ChangesListener> changesListeners = new ArrayList<>();
Expand Down Expand Up @@ -213,6 +215,17 @@ protected void parseImpl() {
parseSocksProxyOptions();

notifyChangesListeners();

legacyRemoveCacheHeaders = getBoolean(BASE_KEY + ".legacy.removeCacheHeaders", false);
}

/**
* Not part of the public API.
*
* @return {@code false} by default.
*/
public boolean isLegacyRemoveCacheHeaders() {
return legacyRemoveCacheHeaders;
}

private void migrateCoreConfigs() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
import org.zaproxy.addon.network.internal.server.http.handlers.ConnectReceivedHandler;
import org.zaproxy.addon.network.internal.server.http.handlers.DecodeResponseHandler;
import org.zaproxy.addon.network.internal.server.http.handlers.HttpSenderHandler;
import org.zaproxy.addon.network.internal.server.http.handlers.LegacyNoCacheRequestHandler;
import org.zaproxy.addon.network.internal.server.http.handlers.LegacyProxyListenerHandler;
import org.zaproxy.addon.network.internal.server.http.handlers.RemoveAcceptEncodingHandler;
import org.zaproxy.addon.network.internal.ui.LocalServerInfoLabel;
Expand Down Expand Up @@ -152,6 +153,7 @@ public class ExtensionNetwork extends ExtensionAdaptor implements CommandLineLis
private org.parosproxy.paros.network.ConnectionParam legacyConnectionOptions;

private LegacyProxyListenerHandler legacyProxyListenerHandler;
private LegacyNoCacheRequestHandler legacyNoCacheRequestHandler;
private Object syncGroups = new Object();
private boolean groupsInitiated;
private NioEventLoopGroup mainEventLoopGroup;
Expand Down Expand Up @@ -476,6 +478,8 @@ public void hook(ExtensionHook extensionHook) {

legacyProxyListenerHandler = new LegacyProxyListenerHandler();
Control.getSingleton().getExtensionLoader().addProxyServer(legacyProxyListenerHandler);
legacyNoCacheRequestHandler =
new LegacyNoCacheRequestHandler(getModel(), connectionOptions);

extensionHook.addCommandLine(createCommandLineArgs());

Expand Down Expand Up @@ -799,6 +803,7 @@ private LocalServer createLocalServer(LocalServerConfig config) {
serverCertificateService,
legacyProxyListenerHandler,
passThroughHandler,
legacyNoCacheRequestHandler,
httpSenderHandler,
new LocalServerConfig(config, aliasChecker),
serialiseForBreak,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.zaproxy.addon.network.internal.server.http.handlers.ConnectReceivedHandler;
import org.zaproxy.addon.network.internal.server.http.handlers.DecodeResponseHandler;
import org.zaproxy.addon.network.internal.server.http.handlers.HttpSenderHandler;
import org.zaproxy.addon.network.internal.server.http.handlers.LegacyNoCacheRequestHandler;
import org.zaproxy.addon.network.internal.server.http.handlers.LegacyProxyListenerHandler;
import org.zaproxy.addon.network.internal.server.http.handlers.RemoveAcceptEncodingHandler;
import org.zaproxy.addon.network.internal.server.http.handlers.ZapApiHandler;
Expand All @@ -45,6 +46,7 @@ public class LocalServer extends HttpServer {

private final Executor executor;
private final LegacyProxyListenerHandler legacyHandler;
private final LegacyNoCacheRequestHandler legacyNoCacheRequestHandler;
private final PassThroughHandler passThroughHandler;
private final HttpSenderHandler httpSenderHandler;

Expand All @@ -67,6 +69,7 @@ public class LocalServer extends HttpServer {
* @param certificateService the certificate service.
* @param legacyHandler the handler for legacy (core) listeners.
* @param passThroughHandler the pass-through handler.
* @param legacyNoCacheRequestHandler the handler that removes cache related headers.
* @param httpSenderHandler the HTTP Sender handler.
* @param serverConfig the server configuration
* @param serialiseState the serialisation state.
Expand All @@ -79,6 +82,7 @@ public LocalServer(
ServerCertificateService certificateService,
LegacyProxyListenerHandler legacyHandler,
PassThroughHandler passThroughHandler,
LegacyNoCacheRequestHandler legacyNoCacheRequestHandler,
HttpSenderHandler httpSenderHandler,
LocalServerConfig serverConfig,
SerialiseState serialiseState,
Expand All @@ -87,6 +91,7 @@ public LocalServer(
this.executor = executor;
this.legacyHandler = legacyHandler;
this.passThroughHandler = Objects.requireNonNull(passThroughHandler);
this.legacyNoCacheRequestHandler = legacyNoCacheRequestHandler;
this.httpSenderHandler = httpSenderHandler;
this.serverConfig = Objects.requireNonNull(serverConfig);

Expand All @@ -111,6 +116,7 @@ private MainServerHandler createLocalServerHandler() {
aliasRewriteHandler,
zapApiHandler,
CloseOnRecursiveRequestHandler.getInstance(),
legacyNoCacheRequestHandler,
removeAcceptEncodingHandler,
decodeResponseHandler,
legacyHandler,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* 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.network.internal.server.http.handlers;

import org.apache.commons.httpclient.URIException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.parosproxy.paros.db.DatabaseException;
import org.parosproxy.paros.model.Model;
import org.parosproxy.paros.network.HttpHeader;
import org.parosproxy.paros.network.HttpMalformedHeaderException;
import org.parosproxy.paros.network.HttpMessage;
import org.parosproxy.paros.network.HttpRequestHeader;
import org.parosproxy.paros.network.HttpStatusCode;
import org.zaproxy.addon.network.ConnectionOptions;
import org.zaproxy.addon.network.server.HttpMessageHandlerContext;
import org.zaproxy.zap.model.SessionStructure;
import org.zaproxy.zap.model.StructuralNode;

/**
* Legacy undocumented behaviour migrated from core that changes the proxied messages by removing
* cache related headers.
*/
public class LegacyNoCacheRequestHandler extends HttpRequestHandler {

private static final Logger LOGGER = LogManager.getLogger(LegacyNoCacheRequestHandler.class);

private final Model model;
private final ConnectionOptions options;

public LegacyNoCacheRequestHandler(Model model, ConnectionOptions options) {
this.model = model;
this.options = options;
}

@Override
protected void handleRequest(HttpMessageHandlerContext ctx, HttpMessage msg) {
if (!options.isLegacyRemoveCacheHeaders()) {
return;
}

if (HttpRequestHeader.CONNECT.equals(msg.getRequestHeader().getMethod())) {
return;
}

onHttpRequestSend(msg);
}

// Implementation migrated verbatim from core class ProxyListenerLog (v2.13.0).
private boolean onHttpRequestSend(HttpMessage msg) {
// if (msg.getRequestHeader().isImage()) {
// return;
// }

try {
StructuralNode node = SessionStructure.find(model, msg);
if (node != null) {
HttpMessage existingMsg = node.getHistoryReference().getHttpMessage();
// check if a msg of the same type exist
if (existingMsg != null && !existingMsg.getResponseHeader().isEmpty()) {
if (HttpStatusCode.isSuccess(existingMsg.getResponseHeader().getStatusCode())) {
// exist, no modification necessary
return true;
}
}
}
} catch (URIException | DatabaseException | HttpMalformedHeaderException e) {
LOGGER.warn("Failed to check if message already exists:", e);
}

// if not, make sure a new copy will be obtained
if (msg.getRequestHeader().getHeader(HttpHeader.IF_MODIFIED_SINCE) != null) {
msg.getRequestHeader().setHeader(HttpHeader.IF_MODIFIED_SINCE, null);
}

if (msg.getRequestHeader().getHeader(HttpHeader.IF_NONE_MATCH) != null) {
msg.getRequestHeader().setHeader(HttpHeader.IF_NONE_MATCH, null);
}
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,25 @@ void shouldHaveConfigVersionKey() {
assertThat(options.getConfigVersionKey(), is(equalTo("network.connection[@version]")));
}

@Test
void shouldHaveLegacyRemoveCacheHeadersDisabledByDefault() {
// Given
options = new ConnectionOptions();
// When / Then
assertThat(options.isLegacyRemoveCacheHeaders(), is(equalTo(false)));
}

@ParameterizedTest
@ValueSource(booleans = {true, false})
void shouldLoadConfigWithLegacyRemoveCacheHeaders(boolean enabled) {
// Given
config.setProperty("network.connection.legacy.removeCacheHeaders", enabled);
// When
options.load(config);
// Then
assertThat(options.isLegacyRemoveCacheHeaders(), is(equalTo(enabled)));
}

@Test
void shouldHaveDefaultValues() {
// Given
Expand Down

0 comments on commit a616259

Please sign in to comment.