Having an issue with the following rest controller in spring boot version 2.3.4.RELEASE, spring security 5.3.4, spring web 5.2.9:
@PostMapping(
value = "/items/{parentId}/item",
produces = { "application/json" })
default ResponseEntity<Item> createItem(
final @PathVariable("parentId") String parentId)
When calling this endpoint I never get the MissingPathVarialeException thrown, instead I'm getting:
2021-03-31 18:08:27.992 ERROR [itemservice:http-nio-8080-exec-1::] i.f.a.config.GlobalExceptionHandler - Sending error response
org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL contained a potentially malicious String "//"
at org.springframework.security.web.firewall.StrictHttpFirewall.rejectedBlacklistedUrls(StrictHttpFirewall.java:369)
at org.springframework.security.web.firewall.StrictHttpFirewall.getFirewalledRequest(StrictHttpFirewall.java:336)
It seems that the firewall rule is triggered first and the path variables are not evaluated at all. According to documentation this should work with all variables marked with @PathVariable.
ServletRequestBindingException subclass that indicates that a path variable expected in the method parameters of an @RequestMapping method is not present among the URI variables extracted from the URL. Typically that means the URI template does not match the path variable name declared on the method parameter.
Turning off the firewall rule with:
@Bean
HttpFirewall httpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowUrlEncodedDoubleSlash(true);
return firewall;
}
results in a HTTP 404.
Thank you for taking a look.
Comment From: wilkinsona
Spring Security is implemented as a filter that runs before the request reaches Spring MVC's dispatcher servlet. As such, a request that's rejected by Spring Security will be rejected before Spring MVC has a chance to handle the request.
For a MissingPathVariableException
to be thrown, the request has to match a request mapping. When it does, any variable names in the mapping's URI template and the names in the @PathVariable
-annotated method arguments need to match up. If they do not, a MissingPathVariableException
is thrown. In your case, the request doesn't match the mapping so a 404 is returned before any URI variables are considered.
Comment From: leiferksn
Thank you for your quick response.
Yes, I know, that the security comes first. That's why I turned off the firewall rule and the URL still didn't match. Setting the parentId to null in the url didn't work either. The response was still 404. What should the URL be in this case, so that the MissingPathVariableException will get thrown?
Comment From: wilkinsona
Setting it to null
, i.e. /items/null/item
should have been sufficient. I can't say why it wasn't without knowing more about your application. If you would like us to spend some more time investigating, please spend some time providing a complete yet minimal sample that reproduces the problem. You can share it with us by pushing it to a separate repository on GitHub or by zipping it up and attaching it to this issue.
Comment From: leiferksn
Hi again, thanks for responding.
I've created a sample project with no security, and this setting to null doesn't seam to be working. The item is getting created, as if the null is a valid parentItemId.
Comment From: wilkinsona
Thanks for the sample.
parentItemId
is being set to "null"
, rather than a null
value. Spring MVC doesn't have an opinion about whether or not that's a valid parentItemId
. That's for your controller to decide.
As I tried to explain above, for a MissingPathVariableException
to be thrown, you need to have a mismatch between the request mapping's URI template and the name of a @PathVariable
-annotated method argument. You can make your sample throw a MissingPathVariableException
by applying the following change:
diff --git a/src/main/java/net/almaak/missingpathvariable/controllers/ItemsController.java b/src/main/java/net/almaak/missingpathvariable/controllers/ItemsController.java
index 2416002..f3eaec9 100644
--- a/src/main/java/net/almaak/missingpathvariable/controllers/ItemsController.java
+++ b/src/main/java/net/almaak/missingpathvariable/controllers/ItemsController.java
@@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.RestController;
public class ItemsController {
@PostMapping(
- value = "/items/{parentItemId}/item",
+ value = "/items/{parentId}/item",
produces = { "application/json" })
public ResponseEntity<String> createNewItemInParent(@PathVariable String parentItemId) {
try {
The @PostMapping
is defining the variable parentId
while the method argument is looking for one named parentItemId
. When the exception is thrown, the client receives a 500 response as it's a server-side error.