Browse Source

Publish Constants for Firewall Header and Parameter Predicates

Introduced public static final Predicates for allowed header names,
header values, parameter names, and parameter values for building
expressions.

Closes gh-13639
baezzys 1 year ago
parent
commit
4169c0cf36

+ 13 - 4
web/src/main/java/org/springframework/security/web/firewall/StrictHttpFirewall.java

@@ -75,6 +75,7 @@ import org.springframework.util.Assert;
  *
  *
  * @author Rob Winch
  * @author Rob Winch
  * @author Eddú Meléndez
  * @author Eddú Meléndez
+ * @author Jinwoo Bae
  * @since 4.2.4
  * @since 4.2.4
  * @see DefaultHttpFirewall
  * @see DefaultHttpFirewall
  */
  */
@@ -134,13 +135,21 @@ public class StrictHttpFirewall implements HttpFirewall {
 
 
 	private static final Predicate<String> HEADER_VALUE_PREDICATE = (s) -> HEADER_VALUE_PATTERN.matcher(s).matches();
 	private static final Predicate<String> HEADER_VALUE_PREDICATE = (s) -> HEADER_VALUE_PATTERN.matcher(s).matches();
 
 
-	private Predicate<String> allowedHeaderNames = ASSIGNED_AND_NOT_ISO_CONTROL_PREDICATE;
+	private Predicate<String> allowedHeaderNames = ALLOWED_HEADER_NAMES;
 
 
-	private Predicate<String> allowedHeaderValues = HEADER_VALUE_PREDICATE;
+	public static final Predicate<String> ALLOWED_HEADER_NAMES = ASSIGNED_AND_NOT_ISO_CONTROL_PREDICATE;
 
 
-	private Predicate<String> allowedParameterNames = ASSIGNED_AND_NOT_ISO_CONTROL_PREDICATE;
+	private Predicate<String> allowedHeaderValues = ALLOWED_HEADER_VALUES;
 
 
-	private Predicate<String> allowedParameterValues = (value) -> true;
+	public static final Predicate<String> ALLOWED_HEADER_VALUES = HEADER_VALUE_PREDICATE;
+
+	private Predicate<String> allowedParameterNames = ALLOWED_PARAMETER_NAMES;
+
+	public static final Predicate<String> ALLOWED_PARAMETER_NAMES = ASSIGNED_AND_NOT_ISO_CONTROL_PREDICATE;
+
+	private Predicate<String> allowedParameterValues = ALLOWED_PARAMETER_VALUES;
+
+	public static final Predicate<String> ALLOWED_PARAMETER_VALUES = (value) -> true;
 
 
 	public StrictHttpFirewall() {
 	public StrictHttpFirewall() {
 		urlBlocklistsAddAll(FORBIDDEN_SEMICOLON);
 		urlBlocklistsAddAll(FORBIDDEN_SEMICOLON);

+ 38 - 0
web/src/test/java/org/springframework/security/web/firewall/StrictHttpFirewallTests.java

@@ -31,6 +31,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
 /**
 /**
  * @author Rob Winch
  * @author Rob Winch
  * @author Eddú Meléndez
  * @author Eddú Meléndez
+ * @author Jinwoo Bae
  */
  */
 public class StrictHttpFirewallTests {
 public class StrictHttpFirewallTests {
 
 
@@ -723,6 +724,14 @@ public class StrictHttpFirewallTests {
 		assertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> request.getHeader("bad name"));
 		assertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> request.getHeader("bad name"));
 	}
 	}
 
 
+	@Test
+	public void getFirewalledRequestWhenHeaderNameNotAllowedWithAugmentedHeaderNamesThenException() {
+		this.firewall
+			.setAllowedHeaderNames(StrictHttpFirewall.ALLOWED_HEADER_NAMES.and((name) -> !name.equals("bad name")));
+		HttpServletRequest request = this.firewall.getFirewalledRequest(this.request);
+		assertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> request.getHeader("bad name"));
+	}
+
 	@Test
 	@Test
 	public void getFirewalledRequestGetHeaderWhenNotAllowedHeaderValueThenException() {
 	public void getFirewalledRequestGetHeaderWhenNotAllowedHeaderValueThenException() {
 		this.request.addHeader("good name", "bad value");
 		this.request.addHeader("good name", "bad value");
@@ -731,6 +740,15 @@ public class StrictHttpFirewallTests {
 		assertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> request.getHeader("good name"));
 		assertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> request.getHeader("good name"));
 	}
 	}
 
 
+	@Test
+	public void getFirewalledRequestWhenHeaderValueNotAllowedWithAugmentedHeaderValuesThenException() {
+		this.request.addHeader("good name", "bad value");
+		this.firewall.setAllowedHeaderValues(
+				StrictHttpFirewall.ALLOWED_HEADER_VALUES.and((value) -> !value.equals("bad value")));
+		HttpServletRequest request = this.firewall.getFirewalledRequest(this.request);
+		assertThatExceptionOfType(RequestRejectedException.class).isThrownBy(() -> request.getHeader("good name"));
+	}
+
 	@Test
 	@Test
 	public void getFirewalledRequestGetDateHeaderWhenControlCharacterInHeaderNameThenException() {
 	public void getFirewalledRequestGetDateHeaderWhenControlCharacterInHeaderNameThenException() {
 		this.request.addHeader("Bad\0Name", "some value");
 		this.request.addHeader("Bad\0Name", "some value");
@@ -840,6 +858,16 @@ public class StrictHttpFirewallTests {
 			.isThrownBy(() -> request.getParameterValues("Something"));
 			.isThrownBy(() -> request.getParameterValues("Something"));
 	}
 	}
 
 
+	@Test
+	public void getFirewalledRequestWhenParameterValueNotAllowedWithAugmentedParameterValuesThenException() {
+		this.request.addParameter("Something", "bad value");
+		this.firewall.setAllowedParameterValues(
+				StrictHttpFirewall.ALLOWED_PARAMETER_VALUES.and((value) -> !value.equals("bad value")));
+		HttpServletRequest request = this.firewall.getFirewalledRequest(this.request);
+		assertThatExceptionOfType(RequestRejectedException.class)
+			.isThrownBy(() -> request.getParameterValues("Something"));
+	}
+
 	@Test
 	@Test
 	public void getFirewalledRequestGetParameterValuesWhenNotAllowedInParameterNameThenException() {
 	public void getFirewalledRequestGetParameterValuesWhenNotAllowedInParameterNameThenException() {
 		this.firewall.setAllowedParameterNames((value) -> !value.equals("bad name"));
 		this.firewall.setAllowedParameterNames((value) -> !value.equals("bad name"));
@@ -849,6 +877,16 @@ public class StrictHttpFirewallTests {
 			.isThrownBy(() -> request.getParameterValues("bad name"));
 			.isThrownBy(() -> request.getParameterValues("bad name"));
 	}
 	}
 
 
+	@Test
+	public void getFirewalledRequestWhenParameterNameNotAllowedWithAugmentedParameterNamesThenException() {
+		this.request.addParameter("bad name", "good value");
+		this.firewall.setAllowedParameterNames(
+				StrictHttpFirewall.ALLOWED_PARAMETER_NAMES.and((value) -> !value.equals("bad name")));
+		HttpServletRequest request = this.firewall.getFirewalledRequest(this.request);
+		assertThatExceptionOfType(RequestRejectedException.class)
+			.isThrownBy(() -> request.getParameterValues("bad name"));
+	}
+
 	// gh-9598
 	// gh-9598
 	@Test
 	@Test
 	public void getFirewalledRequestGetParameterWhenNameIsNullThenIllegalArgumentException() {
 	public void getFirewalledRequestGetParameterWhenNameIsNullThenIllegalArgumentException() {