diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/json/rpc/JsonRPCProtectionInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/json/rpc/JsonRPCProtectionInterceptor.java index d8b14b970f..7eb0e66027 100644 --- a/core/src/main/java/com/predic8/membrane/core/interceptor/json/rpc/JsonRPCProtectionInterceptor.java +++ b/core/src/main/java/com/predic8/membrane/core/interceptor/json/rpc/JsonRPCProtectionInterceptor.java @@ -21,7 +21,6 @@ import com.predic8.membrane.core.http.Response; import com.predic8.membrane.core.interceptor.AbstractInterceptor; import com.predic8.membrane.core.interceptor.Outcome; -import com.predic8.membrane.core.interceptor.json.rpc.JsonRPCValidator.PayloadType; import com.predic8.membrane.core.interceptor.json.rpc.JsonRPCValidator.RequestValidationResult; import com.predic8.membrane.core.interceptor.json.rpc.JsonRPCValidator.ResponseValidationContext; import com.predic8.membrane.core.interceptor.json.rpc.JsonRPCValidator.ValidationError; @@ -37,13 +36,12 @@ import static com.predic8.membrane.core.http.Response.statusCode; import static com.predic8.membrane.core.interceptor.Interceptor.Flow.REQUEST; import static com.predic8.membrane.core.interceptor.Interceptor.Flow.RESPONSE; +import static com.predic8.membrane.core.interceptor.Outcome.ABORT; import static com.predic8.membrane.core.interceptor.Outcome.CONTINUE; -import static com.predic8.membrane.core.interceptor.Outcome.RETURN; import static com.predic8.membrane.core.interceptor.json.rpc.JsonRPCValidator.PayloadType.BATCH; -import static com.predic8.membrane.core.interceptor.json.rpc.JsonRPCValidator.PayloadType.SINGLE; import static com.predic8.membrane.core.interceptor.json.rpc.JsonRPCValidator.getPayloadType; -import static java.util.EnumSet.of; import static com.predic8.membrane.core.jsonrpc.JSONRPCResponse.ERR_INVALID_REQUEST; +import static java.util.EnumSet.of; /** * @topic 3. Security and Validation @@ -158,7 +156,7 @@ private Outcome rejectRequest(Exchange exc, ValidationError error) { } log.info("Rejected JSON-RPC request: {}", error.message()); exc.setResponse(createErrorResponse(error)); - return RETURN; + return ABORT; } private Outcome rejectResponse(Exchange exc, ValidationError error) { @@ -167,7 +165,7 @@ private Outcome rejectResponse(Exchange exc, ValidationError error) { } log.info("Rejected JSON-RPC response: {}", error.message()); exc.setResponse(createErrorResponse(error)); - return RETURN; + return ABORT; } /** diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/mcp/MCPProtectionInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/mcp/MCPProtectionInterceptor.java index e0f6bd88a3..04b7986c42 100644 --- a/core/src/main/java/com/predic8/membrane/core/interceptor/mcp/MCPProtectionInterceptor.java +++ b/core/src/main/java/com/predic8/membrane/core/interceptor/mcp/MCPProtectionInterceptor.java @@ -36,11 +36,11 @@ import static com.predic8.membrane.core.http.MimeType.APPLICATION_JSON; import static com.predic8.membrane.core.http.Request.METHOD_POST; import static com.predic8.membrane.core.http.Response.statusCode; -import static com.predic8.membrane.core.jsonrpc.JSONRPCRequest.parse; import static com.predic8.membrane.core.interceptor.Interceptor.Flow.REQUEST; import static com.predic8.membrane.core.interceptor.Interceptor.Flow.RESPONSE; +import static com.predic8.membrane.core.interceptor.Outcome.ABORT; import static com.predic8.membrane.core.interceptor.Outcome.CONTINUE; -import static com.predic8.membrane.core.interceptor.Outcome.RETURN; +import static com.predic8.membrane.core.jsonrpc.JSONRPCRequest.parse; import static com.predic8.membrane.core.jsonrpc.JSONRPCResponse.ERR_INVALID_REQUEST; import static com.predic8.membrane.core.jsonrpc.JSONRPCResponse.error; import static com.predic8.membrane.core.mcp.MCPToolsList.METHOD; @@ -232,10 +232,10 @@ private Outcome reject(Exchange exc, ValidationError error, boolean addAllowHead log.info("Rejected MCP request: {}", error.message()); if (error.notification()) { exc.setResponse(statusCode(error.httpStatus()).bodyEmpty().build()); - return RETURN; + return ABORT; } exc.setResponse(createErrorResponse(error, addAllowHeader)); - return RETURN; + return ABORT; } private Response createErrorResponse(ValidationError error, boolean addAllowHeader) { diff --git a/core/src/test/java/com/predic8/membrane/core/interceptor/json/rpc/JsonRPCProtectionAbortFlowTest.java b/core/src/test/java/com/predic8/membrane/core/interceptor/json/rpc/JsonRPCProtectionAbortFlowTest.java new file mode 100644 index 0000000000..80f3bab284 --- /dev/null +++ b/core/src/test/java/com/predic8/membrane/core/interceptor/json/rpc/JsonRPCProtectionAbortFlowTest.java @@ -0,0 +1,41 @@ +/* Copyright 2026 predic8 GmbH, www.predic8.com + + 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 com.predic8.membrane.core.interceptor.json.rpc; + +import com.predic8.membrane.core.interceptor.flow.invocation.AbstractInterceptorFlowTest; +import org.junit.jupiter.api.Test; + +import static com.predic8.membrane.core.interceptor.flow.invocation.FlowTestInterceptors.A; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * A rejection must put the exchange into the abort flow, so that the interceptors before + * jsonRPCProtection see handleAbort instead of handleResponse. See + * {@link com.predic8.membrane.core.interceptor.flow.invocation.InterceptorFlowTest} for the + * notation: {@code >a} request flow, {@code