Skip to content

Commit

Permalink
Add methods to augment allowed headers and parameters in StrictHttpFi…
Browse files Browse the repository at this point in the history
…rewall

Introduce `addAllowedHeaderNames`, `addAllowedHeaderValues`, `addAllowedParameterNames`, and `addAllowedParameterValues` to the StrictHttpFirewall class. These methods allow users to augment the existing sets of allowable names and values without replacing the default security configurations.
  • Loading branch information
baezzys committed May 15, 2024
1 parent 1fbfaa1 commit ca559ab
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
*
* @author Rob Winch
* @author Eddú Meléndez
* @author Jinwoo Bae
* @since 4.2.4
* @see DefaultHttpFirewall
*/
Expand Down Expand Up @@ -493,6 +494,54 @@ public void setAllowedHostnames(Predicate<String> allowedHostnames) {
this.allowedHostnames = allowedHostnames;
}

/**
* Adds a custom predicate to expand the set of allowed header names. This method
* combines the new predicate with the existing allowed header names using a logical
* AND, allowing additional header names as specified by the predicate.
* @param additionalHeaderNames the predicate that defines additional allowed header
* names.
*/
public void addAllowedHeaderNames(Predicate<String> additionalHeaderNames) {
Assert.notNull(additionalHeaderNames, "additionalHeaderNames cannot be null");
this.allowedHeaderNames = this.allowedHeaderNames.and(additionalHeaderNames);
}

/**
* Adds a custom predicate to expand the set of allowed header values. This method
* combines the new predicate with the existing allowed header values using a logical
* AND, allowing additional header values as specified by the predicate.
* @param additionalHeaderValues the predicate that defines additional allowed header
* values.
*/
public void addAllowedHeaderValues(Predicate<String> additionalHeaderValues) {
Assert.notNull(additionalHeaderValues, "additionalHeaderValues cannot be null");
this.allowedHeaderValues = this.allowedHeaderValues.and(additionalHeaderValues);
}

/**
* Adds a custom predicate to expand the set of allowed parameter names. This method
* combines the new predicate with the existing allowed parameter names using a
* logical AND, allowing additional parameter names as specified by the predicate.
* @param additionalParameterNames the predicate that defines additional allowed
* parameter names.
*/
public void addAllowedParameterNames(Predicate<String> additionalParameterNames) {
Assert.notNull(additionalParameterNames, "additionalParameterNames cannot be null");
this.allowedParameterNames = this.allowedParameterNames.and(additionalParameterNames);
}

/**
* Adds a custom predicate to expand the set of allowed parameter values. This method
* combines the new predicate with the existing allowed parameter values using a
* logical AND, allowing additional parameter values as specified by the predicate.
* @param additionalParameterValues the predicate that defines additional allowed
* parameter values.
*/
public void addAllowedParameterValues(Predicate<String> additionalParameterValues) {
Assert.notNull(additionalParameterValues, "additionalParameterValues cannot be null");
this.allowedParameterValues = this.allowedParameterValues.and(additionalParameterValues);
}

private void urlBlocklistsAddAll(Collection<String> values) {
this.encodedUrlBlocklist.addAll(values);
this.decodedUrlBlocklist.addAll(values);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
/**
* @author Rob Winch
* @author Eddú Meléndez
* @author Jinwoo Bae
*/
public class StrictHttpFirewallTests {

Expand Down Expand Up @@ -723,6 +724,15 @@ public void getFirewalledRequestGetHeaderWhenNotAllowedHeaderNameThenException()
assertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> request.getHeader("bad name"));
}

@Test
public void getFirewalledRequestGetHeaderWhenAddNotAllowedHeaderNameThenException() {
this.firewall.setAllowedHeaderNames((name) -> !name.equals("bad name"));
this.firewall.addAllowedHeaderNames((name) -> !name.equals("worst name"));
HttpServletRequest request = this.firewall.getFirewalledRequest(this.request);
assertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> request.getHeader("bad name"));
assertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> request.getHeader("worst name"));
}

@Test
public void getFirewalledRequestGetHeaderWhenNotAllowedHeaderValueThenException() {
this.request.addHeader("good name", "bad value");
Expand All @@ -731,6 +741,17 @@ public void getFirewalledRequestGetHeaderWhenNotAllowedHeaderValueThenException(
assertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> request.getHeader("good name"));
}

@Test
public void getFirewalledRequestGetHeaderWhenAddNotAllowedHeaderValueThenException() {
this.request.addHeader("good name", "bad value");
this.request.addHeader("best name", "worst value");
this.firewall.setAllowedHeaderValues((value) -> !value.equals("bad value"));
this.firewall.addAllowedHeaderValues((value) -> !value.equals("worst value"));
HttpServletRequest request = this.firewall.getFirewalledRequest(this.request);
assertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> request.getHeader("good name"));
assertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> request.getHeader("best name"));
}

@Test
public void getFirewalledRequestGetDateHeaderWhenControlCharacterInHeaderNameThenException() {
this.request.addHeader("Bad\0Name", "some value");
Expand Down Expand Up @@ -840,6 +861,18 @@ public void getFirewalledRequestGetParameterValuesWhenNotAllowedInParameterValue
.isThrownBy(() -> request.getParameterValues("Something"));
}

@Test
public void getFirewalledRequestGetParameterValuesWhenAddNotAllowedInParameterValueThenException() {
this.firewall.setAllowedParameterValues((value) -> !value.equals("bad value"));
this.firewall.addAllowedParameterValues((value) -> !value.equals("worst value"));
this.request.addParameter("Something", "bad value");
this.request.addParameter("Item", "worst value");
HttpServletRequest request = this.firewall.getFirewalledRequest(this.request);
assertThatExceptionOfType(RequestRejectedException.class)
.isThrownBy(() -> request.getParameterValues("Something"));
assertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> request.getParameterValues("Item"));
}

@Test
public void getFirewalledRequestGetParameterValuesWhenNotAllowedInParameterNameThenException() {
this.firewall.setAllowedParameterNames((value) -> !value.equals("bad name"));
Expand All @@ -849,6 +882,19 @@ public void getFirewalledRequestGetParameterValuesWhenNotAllowedInParameterNameT
.isThrownBy(() -> request.getParameterValues("bad name"));
}

@Test
public void getFirewalledRequestGetParameterValuesWhenAddNotAllowedInParameterNameThenException() {
this.firewall.setAllowedParameterNames((value) -> !value.equals("bad name"));
this.firewall.addAllowedParameterNames((value) -> !value.equals("worst name"));
this.request.addParameter("bad name", "good value");
this.request.addParameter("worst name", "best value");
HttpServletRequest request = this.firewall.getFirewalledRequest(this.request);
assertThatExceptionOfType(RequestRejectedException.class)
.isThrownBy(() -> request.getParameterValues("bad name"));
assertThatExceptionOfType(RequestRejectedException.class)
.isThrownBy(() -> request.getParameterValues("worst name"));
}

// gh-9598
@Test
public void getFirewalledRequestGetParameterWhenNameIsNullThenIllegalArgumentException() {
Expand Down

0 comments on commit ca559ab

Please sign in to comment.