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

@@ -90,7 +90,7 @@ public final class SecurityJackson2Modules {
if (mapper != null) {
TypeResolverBuilder<?> typeBuilder = mapper.getDeserializationConfig().getDefaultTyper(null);
if (typeBuilder == null) {
mapper.setDefaultTyping(createWhitelistedDefaultTyping());
mapper.setDefaultTyping(createAllowlistedDefaultTyping());
}
}
}
@@ -148,11 +148,11 @@ public final class SecurityJackson2Modules {
}
/**
* Creates a TypeResolverBuilder that performs whitelisting.
* @return a TypeResolverBuilder that performs whitelisting.
* Creates a TypeResolverBuilder that restricts allowed types.
* @return a TypeResolverBuilder that restricts allowed types.
*/
private static TypeResolverBuilder<? extends TypeResolverBuilder> createWhitelistedDefaultTyping() {
TypeResolverBuilder<? extends TypeResolverBuilder> result = new WhitelistTypeResolverBuilder(ObjectMapper.DefaultTyping.NON_FINAL);
private static TypeResolverBuilder<? extends TypeResolverBuilder> createAllowlistedDefaultTyping() {
TypeResolverBuilder<? extends TypeResolverBuilder> result = new AllowlistTypeResolverBuilder(ObjectMapper.DefaultTyping.NON_FINAL);
result = result.init(JsonTypeInfo.Id.CLASS, null);
result = result.inclusion(JsonTypeInfo.As.PROPERTY);
return result;
@@ -164,9 +164,9 @@ public final class SecurityJackson2Modules {
* and overrides the {@code TypeIdResolver}
* @author Rob Winch
*/
static class WhitelistTypeResolverBuilder extends ObjectMapper.DefaultTypeResolverBuilder {
static class AllowlistTypeResolverBuilder extends ObjectMapper.DefaultTypeResolverBuilder {
WhitelistTypeResolverBuilder(ObjectMapper.DefaultTyping defaultTyping) {
AllowlistTypeResolverBuilder(ObjectMapper.DefaultTyping defaultTyping) {
super(
defaultTyping,
//we do explicit validation in the TypeIdResolver
@@ -182,17 +182,17 @@ public final class SecurityJackson2Modules {
PolymorphicTypeValidator subtypeValidator,
Collection<NamedType> subtypes, boolean forSer, boolean 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
* 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
*/
static class WhitelistTypeIdResolver implements TypeIdResolver {
private static final Set<String> WHITELIST_CLASS_NAMES = Collections.unmodifiableSet(new HashSet(Arrays.asList(
static class AllowlistTypeIdResolver implements TypeIdResolver {
private static final Set<String> ALLOWLIST_CLASS_NAMES = Collections.unmodifiableSet(new HashSet(Arrays.asList(
"java.util.ArrayList",
"java.util.Collections$EmptyList",
"java.util.Collections$EmptyMap",
@@ -209,7 +209,7 @@ public final class SecurityJackson2Modules {
private final TypeIdResolver delegate;
WhitelistTypeIdResolver(TypeIdResolver delegate) {
AllowlistTypeIdResolver(TypeIdResolver delegate) {
this.delegate = delegate;
}
@@ -238,7 +238,7 @@ public final class SecurityJackson2Modules {
DeserializationConfig config = (DeserializationConfig) context.getConfig();
JavaType result = delegate.typeFromId(context, id);
String className = result.getRawClass().getName();
if (isWhitelisted(className)) {
if (isInAllowlist(className)) {
return result;
}
boolean isExplicitMixin = config.findMixInClassFor(result.getRawClass()) != null;
@@ -249,14 +249,14 @@ public final class SecurityJackson2Modules {
if (jacksonAnnotation != null) {
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 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");
}
private boolean isWhitelisted(String id) {
return WHITELIST_CLASS_NAMES.contains(id);
private boolean isInAllowlist(String id) {
return ALLOWLIST_CLASS_NAMES.contains(id);
}
@Override

View File

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