From df195b12f2801b1de7119bd3552afa796ac44889 Mon Sep 17 00:00:00 2001 From: John Niang Date: Fri, 20 Sep 2024 11:16:59 +0800 Subject: [PATCH] Make ApplicationContext inaccessible in ServerWebExchange (#6679) #### What type of PR is this? /kind improvement /area core /area plugin /milestone 2.20.x #### What this PR does / why we need it: Plugins can implement their own RouterFunctions and ControllerMappings, but those might expose root ApplicationContext for plugins, which is not expected. So this PR fixes the insecure access to root ApplicationContext. #### Does this PR introduce a user-facing change? ```release-note None ``` --- .../run/halo/app/config/WebFluxConfig.java | 16 ++++++++++ .../SecureRequestMappingHandlerAdapter.java | 26 ++++++++++++++++ .../halo/app/infra/SecureServerRequest.java | 31 +++++++++++++++++++ .../app/infra/SecureServerWebExchange.java | 25 +++++++++++++++ .../app/plugin/AggregatedRouterFunction.java | 3 +- .../DefaultPluginRouterFunctionRegistry.java | 4 ++- 6 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 application/src/main/java/run/halo/app/infra/SecureRequestMappingHandlerAdapter.java create mode 100644 application/src/main/java/run/halo/app/infra/SecureServerRequest.java create mode 100644 application/src/main/java/run/halo/app/infra/SecureServerWebExchange.java diff --git a/application/src/main/java/run/halo/app/config/WebFluxConfig.java b/application/src/main/java/run/halo/app/config/WebFluxConfig.java index 1a864ca63..22a8b50e1 100644 --- a/application/src/main/java/run/halo/app/config/WebFluxConfig.java +++ b/application/src/main/java/run/halo/app/config/WebFluxConfig.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.Objects; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.web.WebProperties; +import org.springframework.boot.autoconfigure.web.reactive.WebFluxRegistrations; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -33,6 +34,7 @@ import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.ServerResponse; import org.springframework.web.reactive.resource.EncodedResourceResolver; import org.springframework.web.reactive.resource.PathResourceResolver; +import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.reactive.result.view.ViewResolutionResultHandler; import org.springframework.web.reactive.result.view.ViewResolver; import reactor.core.publisher.Mono; @@ -41,6 +43,7 @@ import run.halo.app.console.WebSocketRequestPredicate; import run.halo.app.core.endpoint.WebSocketHandlerMapping; import run.halo.app.core.extension.endpoint.CustomEndpoint; import run.halo.app.core.extension.endpoint.CustomEndpointsBuilder; +import run.halo.app.infra.SecureRequestMappingHandlerAdapter; import run.halo.app.infra.properties.AttachmentProperties; import run.halo.app.infra.properties.HaloProperties; import run.halo.app.plugin.extensionpoint.ExtensionGetter; @@ -67,6 +70,19 @@ public class WebFluxConfig implements WebFluxConfigurer { this.applicationContext = applicationContext; } + @Bean + WebFluxRegistrations webFluxRegistrations() { + return new WebFluxRegistrations() { + @Override + public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() { + // Because we have no chance to customize ServerWebExchangeMethodArgumentResolver, + // we have to use SecureRequestMappingHandlerAdapter to replace a secure + // ServerWebExchange. + return new SecureRequestMappingHandlerAdapter(); + } + }; + } + @Bean ServerResponse.Context context(CodecConfigurer codec, ViewResolutionResultHandler resultHandler) { diff --git a/application/src/main/java/run/halo/app/infra/SecureRequestMappingHandlerAdapter.java b/application/src/main/java/run/halo/app/infra/SecureRequestMappingHandlerAdapter.java new file mode 100644 index 000000000..78b75531d --- /dev/null +++ b/application/src/main/java/run/halo/app/infra/SecureRequestMappingHandlerAdapter.java @@ -0,0 +1,26 @@ +package run.halo.app.infra; + +import org.springframework.lang.NonNull; +import org.springframework.web.reactive.HandlerResult; +import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * Secure request mapping handler adapter. + * + * @author johnniang + * @since 2.20.0 + */ +public class SecureRequestMappingHandlerAdapter extends RequestMappingHandlerAdapter { + + @Override + @NonNull + public Mono handle( + @NonNull ServerWebExchange exchange, + @NonNull Object handler + ) { + return super.handle(new SecureServerWebExchange(exchange), handler); + } + +} diff --git a/application/src/main/java/run/halo/app/infra/SecureServerRequest.java b/application/src/main/java/run/halo/app/infra/SecureServerRequest.java new file mode 100644 index 000000000..851b8140c --- /dev/null +++ b/application/src/main/java/run/halo/app/infra/SecureServerRequest.java @@ -0,0 +1,31 @@ +package run.halo.app.infra; + +import org.springframework.lang.NonNull; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.support.ServerRequestWrapper; +import org.springframework.web.server.ServerWebExchange; + +/** + * Secure server request without application context available. + * + * @author johnniang + * @since 2.20.0 + */ +public class SecureServerRequest extends ServerRequestWrapper { + + /** + * Create a new {@code ServerRequestWrapper} that wraps the given request. + * + * @param delegate the request to wrap + */ + public SecureServerRequest(ServerRequest delegate) { + super(delegate); + } + + @Override + @NonNull + public ServerWebExchange exchange() { + return new SecureServerWebExchange(super.exchange()); + } + +} diff --git a/application/src/main/java/run/halo/app/infra/SecureServerWebExchange.java b/application/src/main/java/run/halo/app/infra/SecureServerWebExchange.java new file mode 100644 index 000000000..4d244532f --- /dev/null +++ b/application/src/main/java/run/halo/app/infra/SecureServerWebExchange.java @@ -0,0 +1,25 @@ +package run.halo.app.infra; + +import org.springframework.context.ApplicationContext; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.ServerWebExchangeDecorator; + +/** + * Secure server web exchange without application context available. + * + * @author johnniang + * @since 2.20.0 + */ +public class SecureServerWebExchange extends ServerWebExchangeDecorator { + + public SecureServerWebExchange(ServerWebExchange delegate) { + super(delegate); + } + + @Override + public ApplicationContext getApplicationContext() { + // Always return null to prevent access to application context + return null; + } + +} diff --git a/application/src/main/java/run/halo/app/plugin/AggregatedRouterFunction.java b/application/src/main/java/run/halo/app/plugin/AggregatedRouterFunction.java index 396c2d49d..6dbbbd484 100644 --- a/application/src/main/java/run/halo/app/plugin/AggregatedRouterFunction.java +++ b/application/src/main/java/run/halo/app/plugin/AggregatedRouterFunction.java @@ -9,6 +9,7 @@ import org.springframework.web.reactive.function.server.ServerResponse; import reactor.core.publisher.Mono; import run.halo.app.core.extension.endpoint.CustomEndpoint; import run.halo.app.core.extension.endpoint.CustomEndpointsBuilder; +import run.halo.app.infra.SecureServerRequest; /** * Aggregated router function built from all custom endpoints. @@ -28,7 +29,7 @@ public class AggregatedRouterFunction implements RouterFunction @Override public Mono> route(ServerRequest request) { - return aggregated.route(request); + return aggregated.route(new SecureServerRequest(request)); } @Override diff --git a/application/src/main/java/run/halo/app/plugin/DefaultPluginRouterFunctionRegistry.java b/application/src/main/java/run/halo/app/plugin/DefaultPluginRouterFunctionRegistry.java index 602fd5606..547a07a54 100644 --- a/application/src/main/java/run/halo/app/plugin/DefaultPluginRouterFunctionRegistry.java +++ b/application/src/main/java/run/halo/app/plugin/DefaultPluginRouterFunctionRegistry.java @@ -11,6 +11,7 @@ import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.reactive.function.server.ServerResponse; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import run.halo.app.infra.SecureServerRequest; /** * A composite {@link RouterFunction} implementation for plugin. @@ -31,8 +32,9 @@ public class DefaultPluginRouterFunctionRegistry @Override @NonNull public Mono> route(@NonNull ServerRequest request) { + var secureRequest = new SecureServerRequest(request); return Flux.fromIterable(this.routerFunctions) - .concatMap(routerFunction -> routerFunction.route(request)) + .concatMap(routerFunction -> routerFunction.route(secureRequest)) .next(); }