Replace whitelist with allowlist

Issue gh-8676
This commit is contained in:
Rob Winch 2020-06-10 10:08:31 -05:00
parent a907026eae
commit ca1252be94
9 changed files with 51 additions and 47 deletions

View File

@ -2,7 +2,7 @@ buildscript {
dependencies { dependencies {
classpath 'io.spring.gradle:spring-build-conventions:0.0.32.RELEASE' classpath 'io.spring.gradle:spring-build-conventions:0.0.32.RELEASE'
classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion" classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
classpath 'io.spring.nohttp:nohttp-gradle:0.0.2.RELEASE' classpath 'io.spring.nohttp:nohttp-gradle:0.0.5.RELEASE'
classpath "io.freefair.gradle:aspectj-plugin:5.0.1" classpath "io.freefair.gradle:aspectj-plugin:5.0.1"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
} }
@ -39,3 +39,7 @@ subprojects {
options.encoding = "UTF-8" options.encoding = "UTF-8"
} }
} }
nohttp {
allowlistFile = project.file("etc/nohttp/allowlist.lines")
}

View File

@ -90,7 +90,7 @@ public final class SecurityJackson2Modules {
if (mapper != null) { if (mapper != null) {
TypeResolverBuilder<?> typeBuilder = mapper.getDeserializationConfig().getDefaultTyper(null); TypeResolverBuilder<?> typeBuilder = mapper.getDeserializationConfig().getDefaultTyper(null);
if (typeBuilder == null) { if (typeBuilder == null) {
mapper.setDefaultTyping(createWhitelistedDefaultTyping()); mapper.setDefaultTyping(createAllowlistedDefaultTyping());
} }
} }
} }
@ -148,11 +148,11 @@ public final class SecurityJackson2Modules {
} }
/** /**
* Creates a TypeResolverBuilder that performs whitelisting. * Creates a TypeResolverBuilder that restricts allowed types.
* @return a TypeResolverBuilder that performs whitelisting. * @return a TypeResolverBuilder that restricts allowed types.
*/ */
private static TypeResolverBuilder<? extends TypeResolverBuilder> createWhitelistedDefaultTyping() { private static TypeResolverBuilder<? extends TypeResolverBuilder> createAllowlistedDefaultTyping() {
TypeResolverBuilder<? extends TypeResolverBuilder> result = new WhitelistTypeResolverBuilder(ObjectMapper.DefaultTyping.NON_FINAL); TypeResolverBuilder<? extends TypeResolverBuilder> result = new AllowlistTypeResolverBuilder(ObjectMapper.DefaultTyping.NON_FINAL);
result = result.init(JsonTypeInfo.Id.CLASS, null); result = result.init(JsonTypeInfo.Id.CLASS, null);
result = result.inclusion(JsonTypeInfo.As.PROPERTY); result = result.inclusion(JsonTypeInfo.As.PROPERTY);
return result; return result;
@ -164,9 +164,9 @@ public final class SecurityJackson2Modules {
* and overrides the {@code TypeIdResolver} * and overrides the {@code TypeIdResolver}
* @author Rob Winch * @author Rob Winch
*/ */
static class WhitelistTypeResolverBuilder extends ObjectMapper.DefaultTypeResolverBuilder { static class AllowlistTypeResolverBuilder extends ObjectMapper.DefaultTypeResolverBuilder {
WhitelistTypeResolverBuilder(ObjectMapper.DefaultTyping defaultTyping) { AllowlistTypeResolverBuilder(ObjectMapper.DefaultTyping defaultTyping) {
super( super(
defaultTyping, defaultTyping,
//we do explicit validation in the TypeIdResolver //we do explicit validation in the TypeIdResolver
@ -182,17 +182,17 @@ public final class SecurityJackson2Modules {
PolymorphicTypeValidator subtypeValidator, PolymorphicTypeValidator subtypeValidator,
Collection<NamedType> subtypes, boolean forSer, boolean forDeser) { Collection<NamedType> subtypes, boolean forSer, boolean forDeser) {
TypeIdResolver result = super.idResolver(config, baseType, subtypeValidator, subtypes, forSer, forDeser); TypeIdResolver result = super.idResolver(config, baseType, subtypeValidator, subtypes, forSer, forDeser);
return new WhitelistTypeIdResolver(result); return new AllowlistTypeIdResolver(result);
} }
} }
/** /**
* A {@link TypeIdResolver} that delegates to an existing implementation and throws an IllegalStateException if the * A {@link TypeIdResolver} that delegates to an existing implementation and throws an IllegalStateException if the
* class being looked up is not whitelisted, does not provide an explicit mixin, and is not annotated with Jackson * class being looked up is not in the allowlist, does not provide an explicit mixin, and is not annotated with Jackson
* mappings. See https://github.com/spring-projects/spring-security/issues/4370 * mappings. See https://github.com/spring-projects/spring-security/issues/4370
*/ */
static class WhitelistTypeIdResolver implements TypeIdResolver { static class AllowlistTypeIdResolver implements TypeIdResolver {
private static final Set<String> WHITELIST_CLASS_NAMES = Collections.unmodifiableSet(new HashSet(Arrays.asList( private static final Set<String> ALLOWLIST_CLASS_NAMES = Collections.unmodifiableSet(new HashSet(Arrays.asList(
"java.util.ArrayList", "java.util.ArrayList",
"java.util.Collections$EmptyList", "java.util.Collections$EmptyList",
"java.util.Collections$EmptyMap", "java.util.Collections$EmptyMap",
@ -209,7 +209,7 @@ public final class SecurityJackson2Modules {
private final TypeIdResolver delegate; private final TypeIdResolver delegate;
WhitelistTypeIdResolver(TypeIdResolver delegate) { AllowlistTypeIdResolver(TypeIdResolver delegate) {
this.delegate = delegate; this.delegate = delegate;
} }
@ -238,7 +238,7 @@ public final class SecurityJackson2Modules {
DeserializationConfig config = (DeserializationConfig) context.getConfig(); DeserializationConfig config = (DeserializationConfig) context.getConfig();
JavaType result = delegate.typeFromId(context, id); JavaType result = delegate.typeFromId(context, id);
String className = result.getRawClass().getName(); String className = result.getRawClass().getName();
if (isWhitelisted(className)) { if (isInAllowlist(className)) {
return result; return result;
} }
boolean isExplicitMixin = config.findMixInClassFor(result.getRawClass()) != null; boolean isExplicitMixin = config.findMixInClassFor(result.getRawClass()) != null;
@ -249,14 +249,14 @@ public final class SecurityJackson2Modules {
if (jacksonAnnotation != null) { if (jacksonAnnotation != null) {
return result; return result;
} }
throw new IllegalArgumentException("The class with " + id + " and name of " + className + " is not whitelisted. " + throw new IllegalArgumentException("The class with " + id + " and name of " + className + " is not in the allowlist. " +
"If you believe this class is safe to deserialize, please provide an explicit mapping using Jackson annotations or by providing a Mixin. " + "If you believe this class is safe to deserialize, please provide an explicit mapping using Jackson annotations or by providing a Mixin. " +
"If the serialization is only done by a trusted source, you can also enable default typing. " + "If the serialization is only done by a trusted source, you can also enable default typing. " +
"See https://github.com/spring-projects/spring-security/issues/4370 for details"); "See https://github.com/spring-projects/spring-security/issues/4370 for details");
} }
private boolean isWhitelisted(String id) { private boolean isInAllowlist(String id) {
return WHITELIST_CLASS_NAMES.contains(id); return ALLOWLIST_CLASS_NAMES.contains(id);
} }
@Override @Override

View File

@ -44,20 +44,20 @@ public class SecurityJackson2ModulesTests {
} }
@Test @Test
public void readValueWhenNotWhitelistedOrMappedThenThrowsException() { public void readValueWhenNotAllowedOrMappedThenThrowsException() {
String content = "{\"@class\":\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotWhitelisted\",\"property\":\"bar\"}"; String content = "{\"@class\":\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotAllowlisted\",\"property\":\"bar\"}";
assertThatThrownBy(() -> { assertThatThrownBy(() -> {
mapper.readValue(content, Object.class); mapper.readValue(content, Object.class);
} }
).hasStackTraceContaining("whitelisted"); ).hasStackTraceContaining("allowlist");
} }
@Test @Test
public void readValueWhenExplicitDefaultTypingAfterSecuritySetupThenReadsAsSpecificType() throws Exception { public void readValueWhenExplicitDefaultTypingAfterSecuritySetupThenReadsAsSpecificType() throws Exception {
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
String content = "{\"@class\":\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotWhitelisted\",\"property\":\"bar\"}"; String content = "{\"@class\":\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotAllowlisted\",\"property\":\"bar\"}";
assertThat(mapper.readValue(content, Object.class)).isInstanceOf(NotWhitelisted.class); assertThat(mapper.readValue(content, Object.class)).isInstanceOf(NotAllowlisted.class);
} }
@Test @Test
@ -65,29 +65,29 @@ public class SecurityJackson2ModulesTests {
mapper = new ObjectMapper(); mapper = new ObjectMapper();
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
SecurityJackson2Modules.enableDefaultTyping(mapper); SecurityJackson2Modules.enableDefaultTyping(mapper);
String content = "{\"@class\":\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotWhitelisted\",\"property\":\"bar\"}"; String content = "{\"@class\":\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotAllowlisted\",\"property\":\"bar\"}";
assertThat(mapper.readValue(content, Object.class)).isInstanceOf(NotWhitelisted.class); assertThat(mapper.readValue(content, Object.class)).isInstanceOf(NotAllowlisted.class);
} }
@Test @Test
public void readValueWhenAnnotatedThenReadsAsSpecificType() throws Exception { public void readValueWhenAnnotatedThenReadsAsSpecificType() throws Exception {
String content = "{\"@class\":\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotWhitelistedButAnnotated\",\"property\":\"bar\"}"; String content = "{\"@class\":\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotAllowlistedButAnnotated\",\"property\":\"bar\"}";
assertThat(mapper.readValue(content, Object.class)).isInstanceOf(NotWhitelistedButAnnotated.class); assertThat(mapper.readValue(content, Object.class)).isInstanceOf(NotAllowlistedButAnnotated.class);
} }
@Test @Test
public void readValueWhenMixinProvidedThenReadsAsSpecificType() throws Exception { public void readValueWhenMixinProvidedThenReadsAsSpecificType() throws Exception {
mapper.addMixIn(NotWhitelisted.class, NotWhitelistedMixin.class); mapper.addMixIn(NotAllowlisted.class, NotAllowlistedMixin.class);
String content = "{\"@class\":\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotWhitelisted\",\"property\":\"bar\"}"; String content = "{\"@class\":\"org.springframework.security.jackson2.SecurityJackson2ModulesTests$NotAllowlisted\",\"property\":\"bar\"}";
assertThat(mapper.readValue(content, Object.class)).isInstanceOf(NotWhitelisted.class); assertThat(mapper.readValue(content, Object.class)).isInstanceOf(NotAllowlisted.class);
} }
@Test @Test
public void readValueWhenHashMapThenReadsAsSpecificType() throws Exception { public void readValueWhenHashMapThenReadsAsSpecificType() throws Exception {
mapper.addMixIn(NotWhitelisted.class, NotWhitelistedMixin.class); mapper.addMixIn(NotAllowlisted.class, NotAllowlistedMixin.class);
String content = "{\"@class\":\"java.util.HashMap\"}"; String content = "{\"@class\":\"java.util.HashMap\"}";
assertThat(mapper.readValue(content, Object.class)).isInstanceOf(HashMap.class); assertThat(mapper.readValue(content, Object.class)).isInstanceOf(HashMap.class);
@ -99,7 +99,7 @@ public class SecurityJackson2ModulesTests {
public @interface NotJacksonAnnotation {} public @interface NotJacksonAnnotation {}
@NotJacksonAnnotation @NotJacksonAnnotation
static class NotWhitelisted { static class NotAllowlisted {
private String property = "bar"; private String property = "bar";
public String getProperty() { public String getProperty() {
@ -111,7 +111,7 @@ public class SecurityJackson2ModulesTests {
} }
@JsonIgnoreType(false) @JsonIgnoreType(false)
static class NotWhitelistedButAnnotated { static class NotAllowlistedButAnnotated {
private String property = "bar"; private String property = "bar";
public String getProperty() { public String getProperty() {
@ -126,7 +126,7 @@ public class SecurityJackson2ModulesTests {
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE) isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
abstract class NotWhitelistedMixin { abstract class NotAllowlistedMixin {
} }
} }

View File

@ -1082,7 +1082,7 @@ In this case, you construct `JwtIssuerReactiveAuthenticationManagerResolver` wit
This approach allows us to add and remove elements from the repository (shown as a `Map` in the snippet) at runtime. This approach allows us to add and remove elements from the repository (shown as a `Map` in the snippet) at runtime.
NOTE: It would be unsafe to simply take any issuer and construct an `ReactiveAuthenticationManager` from it. NOTE: It would be unsafe to simply take any issuer and construct an `ReactiveAuthenticationManager` from it.
The issuer should be one that the code can verify from a trusted source like a whitelist. The issuer should be one that the code can verify from a trusted source like an allowed list of issuers.
[[webflux-oauth2resourceserver-bearertoken-resolver]] [[webflux-oauth2resourceserver-bearertoken-resolver]]
== Bearer Token Resolution == Bearer Token Resolution

View File

@ -1857,7 +1857,7 @@ In this case, you construct `JwtIssuerAuthenticationManagerResolver` with a stra
This approach allows us to add and remove elements from the repository (shown as a `Map` in the snippet) at runtime. This approach allows us to add and remove elements from the repository (shown as a `Map` in the snippet) at runtime.
NOTE: It would be unsafe to simply take any issuer and construct an `AuthenticationManager` from it. NOTE: It would be unsafe to simply take any issuer and construct an `AuthenticationManager` from it.
The issuer should be one that the code can verify from a trusted source like a whitelist. The issuer should be one that the code can verify from a trusted source like a list of allowed issuers.
===== Parsing the Claim Only Once ===== Parsing the Claim Only Once
@ -1907,7 +1907,7 @@ public class TenantJWSKeySelector
---- ----
<1> A hypothetical source for tenant information <1> A hypothetical source for tenant information
<2> A cache for `JWKKeySelector`s, keyed by tenant identifier <2> A cache for `JWKKeySelector`s, keyed by tenant identifier
<3> Looking up the tenant is more secure than simply calculating the JWK Set endpoint on the fly - the lookup acts as a tenant whitelist <3> Looking up the tenant is more secure than simply calculating the JWK Set endpoint on the fly - the lookup acts as a list of allowed tenants
<4> Create a `JWSKeySelector` via the types of keys that come back from the JWK Set endpoint - the lazy lookup here means that you don't need to configure all tenants at startup <4> Create a `JWSKeySelector` via the types of keys that come back from the JWK Set endpoint - the lazy lookup here means that you don't need to configure all tenants at startup
The above key selector is a composition of many key selectors. The above key selector is a composition of many key selectors.

View File

@ -45,7 +45,7 @@ import org.springframework.util.Assert;
* *
* To use, this class must be able to determine whether or not the `iss` claim is trusted. Recall that * To use, this class must be able to determine whether or not the `iss` claim is trusted. Recall that
* anyone can stand up an authorization server and issue valid tokens to a resource server. The simplest way * anyone can stand up an authorization server and issue valid tokens to a resource server. The simplest way
* to achieve this is to supply a whitelist of trusted issuers in the constructor. * to achieve this is to supply a list of trusted issuers in the constructor.
* *
* This class derives the Issuer from the `iss` claim found in the {@link HttpServletRequest}'s * This class derives the Issuer from the `iss` claim found in the {@link HttpServletRequest}'s
* <a href="https://tools.ietf.org/html/rfc6750#section-1.2" target="_blank">Bearer Token</a>. * <a href="https://tools.ietf.org/html/rfc6750#section-1.2" target="_blank">Bearer Token</a>.
@ -60,7 +60,7 @@ public final class JwtIssuerAuthenticationManagerResolver implements Authenticat
/** /**
* Construct a {@link JwtIssuerAuthenticationManagerResolver} using the provided parameters * Construct a {@link JwtIssuerAuthenticationManagerResolver} using the provided parameters
* *
* @param trustedIssuers a whitelist of trusted issuers * @param trustedIssuers a list of trusted issuers
*/ */
public JwtIssuerAuthenticationManagerResolver(String... trustedIssuers) { public JwtIssuerAuthenticationManagerResolver(String... trustedIssuers) {
this(Arrays.asList(trustedIssuers)); this(Arrays.asList(trustedIssuers));
@ -69,7 +69,7 @@ public final class JwtIssuerAuthenticationManagerResolver implements Authenticat
/** /**
* Construct a {@link JwtIssuerAuthenticationManagerResolver} using the provided parameters * Construct a {@link JwtIssuerAuthenticationManagerResolver} using the provided parameters
* *
* @param trustedIssuers a whitelist of trusted issuers * @param trustedIssuers a list of trusted issuers
*/ */
public JwtIssuerAuthenticationManagerResolver(Collection<String> trustedIssuers) { public JwtIssuerAuthenticationManagerResolver(Collection<String> trustedIssuers) {
Assert.notEmpty(trustedIssuers, "trustedIssuers cannot be empty"); Assert.notEmpty(trustedIssuers, "trustedIssuers cannot be empty");
@ -82,7 +82,7 @@ public final class JwtIssuerAuthenticationManagerResolver implements Authenticat
* Construct a {@link JwtIssuerAuthenticationManagerResolver} using the provided parameters * Construct a {@link JwtIssuerAuthenticationManagerResolver} using the provided parameters
* *
* Note that the {@link AuthenticationManagerResolver} provided in this constructor will need to * Note that the {@link AuthenticationManagerResolver} provided in this constructor will need to
* verify that the issuer is trusted. This should be done via a whitelist. * verify that the issuer is trusted. This should be done via an allowlist.
* *
* One way to achieve this is with a {@link Map} where the keys are the known issuers: * One way to achieve this is with a {@link Map} where the keys are the known issuers:
* <pre> * <pre>
@ -93,7 +93,7 @@ public final class JwtIssuerAuthenticationManagerResolver implements Authenticat
* (authenticationManagers::get); * (authenticationManagers::get);
* </pre> * </pre>
* *
* The keys in the {@link Map} are the whitelist. * The keys in the {@link Map} are the allowed issuers.
* *
* @param issuerAuthenticationManagerResolver a strategy for resolving the {@link AuthenticationManager} by the issuer * @param issuerAuthenticationManagerResolver a strategy for resolving the {@link AuthenticationManager} by the issuer
*/ */

View File

@ -48,7 +48,7 @@ import org.springframework.web.server.ServerWebExchange;
* *
* To use, this class must be able to determine whether or not the `iss` claim is trusted. Recall that * To use, this class must be able to determine whether or not the `iss` claim is trusted. Recall that
* anyone can stand up an authorization server and issue valid tokens to a resource server. The simplest way * anyone can stand up an authorization server and issue valid tokens to a resource server. The simplest way
* to achieve this is to supply a whitelist of trusted issuers in the constructor. * to achieve this is to supply a list of trusted issuers in the constructor.
* *
* This class derives the Issuer from the `iss` claim found in the {@link ServerWebExchange}'s * This class derives the Issuer from the `iss` claim found in the {@link ServerWebExchange}'s
* <a href="https://tools.ietf.org/html/rfc6750#section-1.2" target="_blank">Bearer Token</a>. * <a href="https://tools.ietf.org/html/rfc6750#section-1.2" target="_blank">Bearer Token</a>.
@ -66,7 +66,7 @@ public final class JwtIssuerReactiveAuthenticationManagerResolver
/** /**
* Construct a {@link JwtIssuerReactiveAuthenticationManagerResolver} using the provided parameters * Construct a {@link JwtIssuerReactiveAuthenticationManagerResolver} using the provided parameters
* *
* @param trustedIssuers a whitelist of trusted issuers * @param trustedIssuers a list of trusted issuers
*/ */
public JwtIssuerReactiveAuthenticationManagerResolver(String... trustedIssuers) { public JwtIssuerReactiveAuthenticationManagerResolver(String... trustedIssuers) {
this(Arrays.asList(trustedIssuers)); this(Arrays.asList(trustedIssuers));
@ -75,7 +75,7 @@ public final class JwtIssuerReactiveAuthenticationManagerResolver
/** /**
* Construct a {@link JwtIssuerReactiveAuthenticationManagerResolver} using the provided parameters * Construct a {@link JwtIssuerReactiveAuthenticationManagerResolver} using the provided parameters
* *
* @param trustedIssuers a whitelist of trusted issuers * @param trustedIssuers a collection of trusted issuers
*/ */
public JwtIssuerReactiveAuthenticationManagerResolver(Collection<String> trustedIssuers) { public JwtIssuerReactiveAuthenticationManagerResolver(Collection<String> trustedIssuers) {
Assert.notEmpty(trustedIssuers, "trustedIssuers cannot be empty"); Assert.notEmpty(trustedIssuers, "trustedIssuers cannot be empty");
@ -87,7 +87,7 @@ public final class JwtIssuerReactiveAuthenticationManagerResolver
* Construct a {@link JwtIssuerReactiveAuthenticationManagerResolver} using the provided parameters * Construct a {@link JwtIssuerReactiveAuthenticationManagerResolver} using the provided parameters
* *
* Note that the {@link ReactiveAuthenticationManagerResolver} provided in this constructor will need to * Note that the {@link ReactiveAuthenticationManagerResolver} provided in this constructor will need to
* verify that the issuer is trusted. This should be done via a whitelist. * verify that the issuer is trusted. This should be done via an allowed list of issuers.
* *
* One way to achieve this is with a {@link Map} where the keys are the known issuers: * One way to achieve this is with a {@link Map} where the keys are the known issuers:
* <pre> * <pre>
@ -98,7 +98,7 @@ public final class JwtIssuerReactiveAuthenticationManagerResolver
* (issuer -> Mono.justOrEmpty(authenticationManagers.get(issuer)); * (issuer -> Mono.justOrEmpty(authenticationManagers.get(issuer));
* </pre> * </pre>
* *
* The keys in the {@link Map} are the whitelist. * The keys in the {@link Map} are the trusted issuers.
* *
* @param issuerAuthenticationManagerResolver a strategy for resolving the {@link ReactiveAuthenticationManager} * @param issuerAuthenticationManagerResolver a strategy for resolving the {@link ReactiveAuthenticationManager}
* by the issuer * by the issuer

View File

@ -358,7 +358,7 @@ public class StrictHttpFirewall implements HttpFirewall {
if (!this.allowedHttpMethods.contains(request.getMethod())) { if (!this.allowedHttpMethods.contains(request.getMethod())) {
throw new RequestRejectedException("The request was rejected because the HTTP method \"" + throw new RequestRejectedException("The request was rejected because the HTTP method \"" +
request.getMethod() + request.getMethod() +
"\" was not included within the whitelist " + "\" was not included within the list of allowed HTTP methods " +
this.allowedHttpMethods); this.allowedHttpMethods);
} }
} }