CORDA-601 - Carpenter should respect whitelist

The class carpenter should refuse to carpent classes that are not
whitelisted or marked as CordaSerializable. This prevents any security
issue where a malicious message could indicate a class had a member of
some type that on construction did something bad. By respecting the
whitelist we avoid this.

As the carpeter annotates anythign it constructs as CordaSerializable,
it will always be able to carpent classes that contain memebrs that were
unknown, and thus unannotated, carpented classes
This commit is contained in:
Katelyn Baker
2017-09-25 20:37:19 +01:00
parent 334164aa86
commit f59b22ba98
5 changed files with 174 additions and 34 deletions

View File

@ -0,0 +1,100 @@
package net.corda.nodeapi.internal.serialization.carpenter
import net.corda.core.serialization.ClassWhitelist
import net.corda.core.serialization.CordaSerializable
import org.assertj.core.api.Assertions
import org.junit.Test
import java.io.NotSerializableException
class ClassCarpenterWhitelistTest {
// whitelisting a class on the class path will mean we will carpente up a class that
// contains it as a member
@Test
fun whitelisted() {
data class A(val a: Int)
class WL : ClassWhitelist {
private val allowedClasses = hashSetOf<String>(
A::class.java.name
)
override fun hasListed(type: Class<*>): Boolean = type.name in allowedClasses
}
val cc = ClassCarpenter(whitelist = WL())
// if this works, the test works, if it throws then we're in a world of pain, we could
// go further but there are a lot of other tests that test weather we can build
// carpented objects
cc.build(ClassSchema("thing", mapOf("a" to NonNullableField(A::class.java))))
}
// However, a class on the class path that isn't whitelisted we will not create
// an object that contains a member of that type
@Test
fun notWhitelisted() {
data class A(val a: Int)
class WL : ClassWhitelist {
override fun hasListed(type: Class<*>) = false
}
val cc = ClassCarpenter(whitelist = WL())
// Class A isn't on the whitelist, so we should fail to carpent it
Assertions.assertThatThrownBy {
cc.build(ClassSchema("thing", mapOf("a" to NonNullableField(A::class.java))))
}.isInstanceOf(NotSerializableException::class.java)
}
// despite now being whitelisted and on the class path, we will carpent this because
// it's marked as CordaSerializable
@Test
fun notWhitelistedButAnnotated() {
@CordaSerializable data class A(val a: Int)
class WL : ClassWhitelist {
override fun hasListed(type: Class<*>) = false
}
val cc = ClassCarpenter(whitelist = WL())
// again, simply not throwing here is enough to show the test worked and the carpenter
// didn't reject the type even though it wasn't on the whitelist because it was
// annotated properly
cc.build(ClassSchema("thing", mapOf("a" to NonNullableField(A::class.java))))
}
@Test
fun notWhitelistedButCarpented() {
// just have the white list reject *Everything* except ints
class WL : ClassWhitelist {
override fun hasListed(type: Class<*>) = type.name == "int"
}
val cc = ClassCarpenter(whitelist = WL())
val schema1a = ClassSchema("thing1a", mapOf("a" to NonNullableField(Int::class.java)))
// thing 1 won't be set as corda serializable, meaning we won't build schema 2
schema1a.setNotCordaSerializable()
val clazz1a = cc.build(schema1a)
val schema2 = ClassSchema("thing2", mapOf("a" to NonNullableField(clazz1a)))
// thing 2 references thing 1 which wasn't carpented as corda s erializable and thus
// this will fail
Assertions.assertThatThrownBy {
cc.build(schema2)
}.isInstanceOf(NotSerializableException::class.java)
// create a second type of schema1, this time leave it as corda serialzable
val schema1b = ClassSchema("thing1b", mapOf("a" to NonNullableField(Int::class.java)))
val clazz1b = cc.build(schema1b)
// since schema 1b was created as CordaSerializable this will work
val schema2b = ClassSchema("thing2", mapOf("a" to NonNullableField(clazz1b)))
}
}