This is an automated email from the ASF dual-hosted git repository. robertlazarski pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/axis-axis2-java-core.git
commit 8be5bb57231e7c36745d3ae897a7376b22949af1 Author: Robert Lazarski <[email protected]> AuthorDate: Fri Sep 4 08:59:11 2026 -1000 Filter the service listing in the model, not only in the view The servlet listing did exclude hiddenService services, but in listServices.jsp rather than before the model reached the request, so anything else rendering that model would name them. Apply both controls where the model is built, as HTTPTransportReceiver does for the standalone transport, and drop a null service instead of carrying one: isHiddenService would throw on it, and the previous putAll had the same hazard. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> --- .../apache/axis2/transport/http/ListingAgent.java | 13 ++- .../transport/http/ServiceListingFilterTest.java | 111 +++++++++++++++++++++ 2 files changed, 122 insertions(+), 2 deletions(-) diff --git a/modules/transport/http/src/main/java/org/apache/axis2/transport/http/ListingAgent.java b/modules/transport/http/src/main/java/org/apache/axis2/transport/http/ListingAgent.java index cece74534c..a81408c414 100644 --- a/modules/transport/http/src/main/java/org/apache/axis2/transport/http/ListingAgent.java +++ b/modules/transport/http/src/main/java/org/apache/axis2/transport/http/ListingAgent.java @@ -358,8 +358,17 @@ public class ListingAgent extends AbstractAgent { // either: the listing gives its name, EPR and every operation, which // is what those routes were hidden to withhold. for (java.util.Map.Entry<String, AxisService> entry : services.entrySet()) { - if (entry.getValue() == null || entry.getValue().isMetadataExposed()) { - sortedServices.put(entry.getKey(), entry.getValue()); + AxisService service = entry.getValue(); + // Filter here rather than relying on the view: a hidden service + // that reaches the request attributes can still be rendered by + // any other consumer of it. hiddenService and metadata exposure + // are separate controls and both withhold the listing, which is + // the same pair HTTPTransportReceiver applies for the standalone + // transport. A null cannot be rendered at all -- isHiddenService + // would throw on it -- so it is dropped rather than carried. + if (service != null && !Utils.isHiddenService(service) + && service.isMetadataExposed()) { + sortedServices.put(entry.getKey(), service); } } } diff --git a/modules/transport/http/src/test/java/org/apache/axis2/transport/http/ServiceListingFilterTest.java b/modules/transport/http/src/test/java/org/apache/axis2/transport/http/ServiceListingFilterTest.java new file mode 100644 index 0000000000..a302276f59 --- /dev/null +++ b/modules/transport/http/src/test/java/org/apache/axis2/transport/http/ServiceListingFilterTest.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.axis2.transport.http; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Map; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import junit.framework.TestCase; + +import org.apache.axis2.Constants; +import org.apache.axis2.context.ConfigurationContext; +import org.apache.axis2.context.ConfigurationContextFactory; +import org.apache.axis2.description.AxisService; + +/** + * The service listing is filtered before it reaches the request attributes rather + * than only in the view, so that a service the operator withheld cannot be rendered + * by any other consumer of the model. + * + * <p>Two separate controls withhold a service: the {@code hiddenService} parameter, + * and {@code exposeServiceMetadata}. The standalone transport's lister applies both, + * and so does this one. + */ +public class ServiceListingFilterTest extends TestCase { + + private ConfigurationContext configContext; + + @Override + protected void setUp() throws Exception { + configContext = ConfigurationContextFactory.createEmptyConfigurationContext(); + + configContext.getAxisConfiguration().addService(new AxisService("Visible")); + + AxisService notExposed = new AxisService("NotExposed"); + notExposed.addParameter(AxisService.EXPOSE_SERVICE_METADATA, "false"); + configContext.getAxisConfiguration().addService(notExposed); + + AxisService hidden = new AxisService("HiddenParam"); + hidden.addParameter(Constants.HIDDEN_SERVICE_PARAM_NAME, "true"); + configContext.getAxisConfiguration().addService(hidden); + } + + @SuppressWarnings("unchecked") + private Map<String, AxisService> listedServices() throws Exception { + HttpServletRequest req = mock(HttpServletRequest.class); + HttpServletResponse res = mock(HttpServletResponse.class); + when(req.getRequestURL()) + .thenReturn(new StringBuffer("http://localhost:8080/axis2/services/")); + final Map<String, AxisService>[] captured = new Map[1]; + // renderView needs a container, so capture the model the JSP would receive. + org.mockito.Mockito.doAnswer(invocation -> { + if ("sortedServices".equals(invocation.getArgument(0))) { + captured[0] = (Map<String, AxisService>) invocation.getArgument(1); + } + return null; + }).when(req).setAttribute(org.mockito.ArgumentMatchers.anyString(), + org.mockito.ArgumentMatchers.any()); + try { + new ListingAgent(configContext).processListServices(req, res); + } catch (Exception rendering) { + // The view cannot render without a container; the model is what matters. + } + assertNotNull("the listing model should have been populated", captured[0]); + return captured[0]; + } + + public void testAnExposedServiceIsListed() throws Exception { + assertTrue(listedServices().containsKey("Visible")); + } + + public void testAServiceWithMetadataExposureDisabledIsNotListed() throws Exception { + assertFalse("the listing names the service, its EPR and its operations", + listedServices().containsKey("NotExposed")); + } + + /** + * The hiddenService parameter is a separate control, and the servlet listing has + * to apply it in the model too -- not only in listServices.jsp. + */ + public void testAServiceHiddenByParameterIsNotListed() throws Exception { + assertFalse(listedServices().containsKey("HiddenParam")); + } + + /** Nothing null reaches the model, since the view would throw on it. */ + public void testNoNullServiceReachesTheModel() throws Exception { + for (AxisService service : listedServices().values()) { + assertNotNull("a null service would break rendering the page", service); + } + } +}
