[java-shib-shared] branch main updated: JSSH-58 - Add servlet request validation for method and content type
Scott Cantor
cantor.2 at osu.edu
Fri Apr 4 19:57:51 UTC 2025
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch main
in repository java-shib-shared.
View the commit online:
http://git.shibboleth.net/view/?p=java-shib-shared.git;a=commit;h=9dcb9b5846b79023b9ab01574cdeb7d0bcb675e8
The following commit(s) were added to refs/heads/main by this push:
new 9dcb9b58 JSSH-58 - Add servlet request validation for method and content type
9dcb9b58 is described below
commit 9dcb9b5846b79023b9ab01574cdeb7d0bcb675e8
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Fri Apr 4 15:57:48 2025 -0400
JSSH-58 - Add servlet request validation for method and content type
https://shibboleth.atlassian.net/browse/JSSH-58
---
...asicHttpServletRequestContentTypeValidator.java | 119 +++++++++++++++++++++
.../BasicHttpServletRequestMethodValidator.java | 79 ++++++++++++++
...BasicHttpServletRequestParametersValidator.java | 4 -
...HttpServletRequestContentTypeValidatorTest.java | 118 ++++++++++++++++++++
...BasicHttpServletRequestMethodValidatorTest.java | 96 +++++++++++++++++
...cHttpServletRequestParametersValidatorTest.java | 3 +-
.../ChainingHttpServletRequestValidatorTest.java | 1 +
.../HttpServletRequestResponseContextTest.java | 1 +
8 files changed, 416 insertions(+), 5 deletions(-)
diff --git a/shib-networking/src/main/java/net/shibboleth/shared/servlet/impl/BasicHttpServletRequestContentTypeValidator.java b/shib-networking/src/main/java/net/shibboleth/shared/servlet/impl/BasicHttpServletRequestContentTypeValidator.java
new file mode 100644
index 00000000..8246c13c
--- /dev/null
+++ b/shib-networking/src/main/java/net/shibboleth/shared/servlet/impl/BasicHttpServletRequestContentTypeValidator.java
@@ -0,0 +1,119 @@
+/*
+ * 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 net.shibboleth.shared.servlet.impl;
+
+import java.util.Collection;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import com.google.common.base.Predicates;
+import com.google.common.net.MediaType;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import net.shibboleth.shared.annotation.constraint.NotLive;
+import net.shibboleth.shared.annotation.constraint.Unmodifiable;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.component.AbstractInitializableComponent;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.StringSupport;
+import net.shibboleth.shared.servlet.HttpServletRequestValidator;
+import net.shibboleth.shared.servlet.HttpServletSupport;
+
+/**
+ * Component that validates the HTTP content type as one of an allowed set.
+ *
+ * <p>Optionally allows for no type to be specified.</p>
+ *
+ * @since 9.2.0
+ */
+public class BasicHttpServletRequestContentTypeValidator extends AbstractInitializableComponent
+ implements HttpServletRequestValidator {
+
+ /** Allowed parameters. */
+ @Nonnull private Set<MediaType> allowedContentTypes;
+
+ /** Whether an absent content type is allowed. */
+ private boolean allowNullContentType;
+
+ /**
+ * Constructor.
+ */
+ public BasicHttpServletRequestContentTypeValidator() {
+ allowedContentTypes = CollectionSupport.emptySet();
+ }
+
+ /**
+ * Get the allowed types.
+ *
+ * @return the allowed types
+ */
+ @Nonnull @Unmodifiable @NotLive public Set<String> getAllowedContentTypes() {
+ return allowedContentTypes.stream()
+ .map(MediaType::toString)
+ .collect(CollectionSupport.nonnullCollector(Collectors.toUnmodifiableSet())).get();
+ }
+
+ /**
+ * Set the allowed types.
+ *
+ * @param types the allowed types
+ */
+ public void setAllowedContentTypes(@Nullable final Collection<String> types) {
+ checkSetterPreconditions();
+ if (types != null) {
+ allowedContentTypes = types.stream()
+ .map(StringSupport::trimOrNull)
+ .filter(Predicates.notNull())
+ .map(MediaType::parse)
+ .collect(CollectionSupport.nonnullCollector(Collectors.toUnmodifiableSet())).get();
+ } else {
+ allowedContentTypes = CollectionSupport.emptySet();
+ }
+ }
+
+ /**
+ * Gets whether to allow an absent content type.
+ *
+ * @return whether to allow an absent content type
+ */
+ public boolean getAllowNullContentType() {
+ return allowNullContentType;
+ }
+
+ /**
+ * Sets whether to allow an absent content type.
+ *
+ * <p>Defaults to false.</p>
+ *
+ * @param flag flag to set
+ */
+ public void setAllowNullContentType(final boolean flag) {
+ allowNullContentType = flag;
+ }
+
+ /** {@inheritDoc} */
+ public void validate(@Nonnull final HttpServletRequest request) throws ServletException {
+ Constraint.isNotNull(request, "HttpServletRequest was null");
+
+ if (!HttpServletSupport.validateContentType(request, allowedContentTypes, allowNullContentType, false)) {
+ throw new ServletException("Content type not in allowed set.");
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/shib-networking/src/main/java/net/shibboleth/shared/servlet/impl/BasicHttpServletRequestMethodValidator.java b/shib-networking/src/main/java/net/shibboleth/shared/servlet/impl/BasicHttpServletRequestMethodValidator.java
new file mode 100644
index 00000000..59d8acf5
--- /dev/null
+++ b/shib-networking/src/main/java/net/shibboleth/shared/servlet/impl/BasicHttpServletRequestMethodValidator.java
@@ -0,0 +1,79 @@
+/*
+ * 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 net.shibboleth.shared.servlet.impl;
+
+import java.util.Collection;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import net.shibboleth.shared.annotation.constraint.NotLive;
+import net.shibboleth.shared.annotation.constraint.Unmodifiable;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.component.AbstractInitializableComponent;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.StringSupport;
+import net.shibboleth.shared.servlet.HttpServletRequestValidator;
+
+/**
+ * Component that validates the HTTP request method as one of an allowed set.
+ *
+ * @since 9.2.0
+ */
+public class BasicHttpServletRequestMethodValidator extends AbstractInitializableComponent
+ implements HttpServletRequestValidator {
+
+ /** Allowed parameters. */
+ @Nonnull private Set<String> allowedMethods;
+
+ /**
+ * Constructor.
+ */
+ public BasicHttpServletRequestMethodValidator() {
+ allowedMethods = CollectionSupport.emptySet();
+ }
+
+ /**
+ * Get the allowed methods.
+ *
+ * @return the allowed methods
+ */
+ @Nonnull @Unmodifiable @NotLive public Set<String> getAllowedMethods() {
+ return allowedMethods;
+ }
+
+ /**
+ * Set the allowed methods.
+ *
+ * @param methods the allowed methods
+ */
+ public void setAllowedMethods(@Nullable final Collection<String> methods) {
+ checkSetterPreconditions();
+ allowedMethods = CollectionSupport.copyToSet(StringSupport.normalizeStringCollection(methods));
+ }
+
+ /** {@inheritDoc} */
+ public void validate(@Nonnull final HttpServletRequest request) throws ServletException {
+ Constraint.isNotNull(request, "HttpServletRequest was null");
+
+ if (!allowedMethods.contains(request.getMethod())) {
+ throw new ServletException("Request method '" + request.getMethod() + "' not in allowed set.");
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/shib-networking/src/main/java/net/shibboleth/shared/servlet/impl/BasicHttpServletRequestParametersValidator.java b/shib-networking/src/main/java/net/shibboleth/shared/servlet/impl/BasicHttpServletRequestParametersValidator.java
index 6704380a..5b175754 100644
--- a/shib-networking/src/main/java/net/shibboleth/shared/servlet/impl/BasicHttpServletRequestParametersValidator.java
+++ b/shib-networking/src/main/java/net/shibboleth/shared/servlet/impl/BasicHttpServletRequestParametersValidator.java
@@ -194,7 +194,6 @@ public class BasicHttpServletRequestParametersValidator extends AbstractInitiali
log.debug("Evaluating request for allowed parameters: {}", getAllowedParameters());
for (final String requestParam : requestParams) {
if (!getAllowedParameters().contains(requestParam)) {
- log.warn("HTTP request contained a disallowed parameter: {}", requestParam);
throw new ServletException("HTTP request contained a disallowed parameter: " + requestParam);
}
}
@@ -205,7 +204,6 @@ public class BasicHttpServletRequestParametersValidator extends AbstractInitiali
log.debug("Evaluating request for required parameters: {}", getRequiredParameters());
for (final String param : getRequiredParameters()) {
if (!requestParams.contains(param)) {
- log.warn("HTTP request did not contain required parameter: {}", param);
throw new ServletException("HTTP request did not contain required parameter: " + param);
}
}
@@ -214,7 +212,6 @@ public class BasicHttpServletRequestParametersValidator extends AbstractInitiali
for (final String param : getUniqueParameters()) {
final String[] values = request.getParameterValues(param);
if (values != null && values.length > 1) {
- log.warn("HTTP request contained {} values for parameter: {}", values.length, param);
throw new ServletException("HTTP request contained multiple values for parameter: " + param);
}
}
@@ -231,7 +228,6 @@ public class BasicHttpServletRequestParametersValidator extends AbstractInitiali
.collect(Collectors.toSet());
if (groupIntersection.size() > 1) {
- log.warn("HTTP request contained mutuallly exclusive parameters: {}", groupIntersection);
throw new ServletException("HTTP request contained mutually exclusive parameters: "
+ groupIntersection);
}
diff --git a/shib-networking/src/test/java/net/shibboleth/shared/servlet/impl/BasicHttpServletRequestContentTypeValidatorTest.java b/shib-networking/src/test/java/net/shibboleth/shared/servlet/impl/BasicHttpServletRequestContentTypeValidatorTest.java
new file mode 100644
index 00000000..bd07c754
--- /dev/null
+++ b/shib-networking/src/test/java/net/shibboleth/shared/servlet/impl/BasicHttpServletRequestContentTypeValidatorTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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 net.shibboleth.shared.servlet.impl;
+
+import java.util.Set;
+
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.testng.Assert;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import jakarta.servlet.ServletException;
+
+/**
+ * Unit test for {@link BasicHttpServletRequestContentTypeValidator}.
+ */
+ at SuppressWarnings("javadoc")
+public class BasicHttpServletRequestContentTypeValidatorTest {
+
+ @DataProvider
+ Object[][] allowedMethodsSetterData() {
+ return new Object[][] {
+ new Object[] { Set.of(),
+ Set.of(),
+ true},
+ new Object[] { Set.of(" application/foo ", " text/bar ", " "),
+ Set.of("application/foo", "text/bar"),
+ false},
+ new Object[] { Set.of(" application/foo; charset=utf-8 ", " text/bar ", " "),
+ Set.of("application/foo; charset=utf-8", "text/bar"),
+ false},
+ };
+ }
+
+ @Test(dataProvider="allowedMethodsSetterData")
+ public void allowedSetter(final Set<String> methods, final Set<String> expected, final boolean allowNull)
+ throws Exception {
+ final BasicHttpServletRequestContentTypeValidator validator = new BasicHttpServletRequestContentTypeValidator();
+ validator.setAllowedContentTypes(methods);
+ validator.setAllowNullContentType(allowNull);
+ validator.initialize();
+
+ Assert.assertEquals(validator.getAllowedContentTypes(), expected);
+ Assert.assertEquals(validator.getAllowNullContentType(), allowNull);
+ }
+
+ @DataProvider
+ Object[][] allowedContentTypesEvalData() {
+ return new Object[][] {
+ new Object[] { "text/xml",
+ Set.of(),
+ false,
+ false},
+ new Object[] { "text/xml",
+ Set.of("text/xml"),
+ false,
+ true},
+ new Object[] { "text/xml; charset=utf-8",
+ Set.of("text/xml"),
+ false,
+ true},
+ new Object[] { "text/xml; charset=utf-8",
+ Set.of("text/xml; charset=iso8859-1"),
+ false,
+ true},
+ new Object[] { null,
+ Set.of("text/xml"),
+ false,
+ false},
+ new Object[] { null,
+ Set.of("text/xml"),
+ true,
+ true},
+ };
+ }
+
+ @Test(dataProvider="allowedContentTypesEvalData")
+ public void requiredEval(final String requestedType, final Set<String> allowedTypes, final boolean allowNull,
+ final boolean valid) throws Exception {
+ BasicHttpServletRequestContentTypeValidator validator = new BasicHttpServletRequestContentTypeValidator();
+ validator.setAllowedContentTypes(allowedTypes);
+ validator.setAllowNullContentType(allowNull);
+ evaluateRequest(validator, requestedType, valid);
+ }
+
+ private void evaluateRequest(final BasicHttpServletRequestContentTypeValidator validator, final String type,
+ final boolean valid) throws Exception{
+
+ final MockHttpServletRequest request = new MockHttpServletRequest();
+ request.setContentType(type);
+
+ validator.initialize();
+
+ try {
+ validator.validate(request);
+ if (!valid) {
+ Assert.fail("Request evaled to valid on invalid type");
+ }
+ } catch (ServletException e) {
+ if (valid) {
+ Assert.fail("Request evaled to invalid on valid type");
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/shib-networking/src/test/java/net/shibboleth/shared/servlet/impl/BasicHttpServletRequestMethodValidatorTest.java b/shib-networking/src/test/java/net/shibboleth/shared/servlet/impl/BasicHttpServletRequestMethodValidatorTest.java
new file mode 100644
index 00000000..32a0fae2
--- /dev/null
+++ b/shib-networking/src/test/java/net/shibboleth/shared/servlet/impl/BasicHttpServletRequestMethodValidatorTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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 net.shibboleth.shared.servlet.impl;
+
+import java.util.Set;
+
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.testng.Assert;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import jakarta.servlet.ServletException;
+
+/**
+ * Unit test for {@link BasicHttpServletRequestMethodValidator}.
+ */
+ at SuppressWarnings("javadoc")
+public class BasicHttpServletRequestMethodValidatorTest {
+
+ @DataProvider
+ Object[][] allowedMethodsSetterData() {
+ return new Object[][] {
+ new Object[] { Set.of(),
+ Set.of()},
+ new Object[] { Set.of(" Foo ", " Bar ", " "),
+ Set.of("Foo", "Bar")},
+ };
+ }
+
+ @Test(dataProvider="allowedMethodsSetterData")
+ public void allowedSetter(final Set<String> methods, final Set<String> expected) throws Exception {
+ final BasicHttpServletRequestMethodValidator validator = new BasicHttpServletRequestMethodValidator();
+ validator.setAllowedMethods(methods);
+ validator.initialize();
+
+ Assert.assertEquals(validator.getAllowedMethods(), expected);
+ }
+
+ @DataProvider
+ Object[][] allowedMethodsEvalData() {
+ return new Object[][] {
+ new Object[] { "GET",
+ Set.of(),
+ false},
+ new Object[] { "POST",
+ Set.of("POST"),
+ true},
+ new Object[] { "POST",
+ Set.of("POST", "GET"),
+ true},
+ new Object[] { "HEAD",
+ Set.of("GET", "POST"),
+ false},
+ };
+ }
+
+ @Test(dataProvider="allowedMethodsEvalData")
+ public void requiredEval(final String requestedMethod, final Set<String> allowedMethods, final boolean valid) throws Exception {
+ BasicHttpServletRequestMethodValidator validator = new BasicHttpServletRequestMethodValidator();
+ validator.setAllowedMethods(allowedMethods);
+ evaluateRequest(validator, requestedMethod, valid);
+ }
+
+ private void evaluateRequest(final BasicHttpServletRequestMethodValidator validator, final String method, final boolean valid)
+ throws Exception{
+
+ final MockHttpServletRequest request = new MockHttpServletRequest();
+ request.setMethod(method);
+
+ validator.initialize();
+
+ try {
+ validator.validate(request);
+ if (!valid) {
+ Assert.fail("Request evaled to valid on invalid method");
+ }
+ } catch (ServletException e) {
+ if (valid) {
+ Assert.fail("Request evaled to invalid on valid method");
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/shib-networking/src/test/java/net/shibboleth/shared/servlet/impl/BasicHttpServletRequestParametersValidatorTest.java b/shib-networking/src/test/java/net/shibboleth/shared/servlet/impl/BasicHttpServletRequestParametersValidatorTest.java
index 9f1613ea..f321261b 100644
--- a/shib-networking/src/test/java/net/shibboleth/shared/servlet/impl/BasicHttpServletRequestParametersValidatorTest.java
+++ b/shib-networking/src/test/java/net/shibboleth/shared/servlet/impl/BasicHttpServletRequestParametersValidatorTest.java
@@ -28,6 +28,7 @@ import net.shibboleth.shared.collection.Pair;
/**
* Unit test for {@link BasicHttpServletRequestParametersValidator}
*/
+ at SuppressWarnings("javadoc")
public class BasicHttpServletRequestParametersValidatorTest {
@DataProvider
@@ -264,7 +265,7 @@ public class BasicHttpServletRequestParametersValidatorTest {
}
} catch (ServletException e) {
if (valid) {
- Assert.fail(String.format("Request evaled to invaid on valid %s params", desc));
+ Assert.fail(String.format("Request evaled to invalid on valid %s params", desc));
}
}
}
diff --git a/shib-networking/src/test/java/net/shibboleth/shared/servlet/impl/ChainingHttpServletRequestValidatorTest.java b/shib-networking/src/test/java/net/shibboleth/shared/servlet/impl/ChainingHttpServletRequestValidatorTest.java
index c4ea9f95..7efd7552 100644
--- a/shib-networking/src/test/java/net/shibboleth/shared/servlet/impl/ChainingHttpServletRequestValidatorTest.java
+++ b/shib-networking/src/test/java/net/shibboleth/shared/servlet/impl/ChainingHttpServletRequestValidatorTest.java
@@ -30,6 +30,7 @@ import net.shibboleth.shared.servlet.HttpServletRequestValidator;
/**
* Unit test for {@link ChainingHttpServletRequestValidator}
*/
+ at SuppressWarnings("javadoc")
public class ChainingHttpServletRequestValidatorTest {
@DataProvider
diff --git a/shib-networking/src/test/java/net/shibboleth/shared/servlet/impl/HttpServletRequestResponseContextTest.java b/shib-networking/src/test/java/net/shibboleth/shared/servlet/impl/HttpServletRequestResponseContextTest.java
index 0035ddcf..3a4aacb8 100644
--- a/shib-networking/src/test/java/net/shibboleth/shared/servlet/impl/HttpServletRequestResponseContextTest.java
+++ b/shib-networking/src/test/java/net/shibboleth/shared/servlet/impl/HttpServletRequestResponseContextTest.java
@@ -28,6 +28,7 @@ import net.shibboleth.shared.logic.ConstraintViolationException;
/**
* Tests for {@link HttpServletRequestResponseContext}.
*/
+ at SuppressWarnings("javadoc")
public class HttpServletRequestResponseContextTest {
private HttpServletRequest request;
private HttpServletResponse response;
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list