Tighten no overlap check. (#4690)

This commit is contained in:
Tudor Malene 2019-01-30 22:01:18 +00:00 committed by GitHub
parent deba96dce9
commit 37eb93fa76
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 47 additions and 7 deletions

View File

@ -50,12 +50,21 @@ class AttachmentsClassLoader(attachments: List<Attachment>, parent: ClassLoader
private val ignoreDirectories = listOf("org/jolokia/", "org/json/simple/")
private val ignorePackages = ignoreDirectories.map { it.replace("/", ".") }
private fun shouldCheckForNoOverlap(path: String, targetPlatformVersion: Int) = when {
// This function attempts to strike a balance between security and usability when it comes to the no-overlap rule.
// TODO - investigate potential exploits.
private fun shouldCheckForNoOverlap(path: String, targetPlatformVersion: Int): Boolean {
require(path.toLowerCase() == path)
require(!path.contains("\\"))
return when {
path.endsWith("/") -> false // Directories (packages) can overlap.
targetPlatformVersion < 4 && ignoreDirectories.any { path.startsWith(it) } -> false // Ignore jolokia and json-simple for old cordapps.
path.endsWith(".class") -> true // All class files need to be unique.
!path.startsWith("meta-inf") -> true // All files outside of Meta-inf need to be unique.
else -> false // This allows overlaps over any non-class files in "Meta-inf".
!path.startsWith("meta-inf") -> true // All files outside of META-INF need to be unique.
(path == "meta-inf/services/net.corda.core.serialization.serializationwhitelist") -> false // Allow overlapping on the SerializationWhitelist.
path.startsWith("meta-inf/services") -> true // Services can't overlap to prevent a malicious party from injecting additional implementations of an interface used by a contract.
else -> false // This allows overlaps over any non-class files in "META-INF" - except 'services'.
}
}
private fun requireNoDuplicates(attachments: List<Attachment>) {

View File

@ -14,6 +14,7 @@ import net.corda.testing.services.MockAttachmentStorage
import org.apache.commons.io.IOUtils
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
import org.junit.Ignore
import org.junit.Test
import java.io.ByteArrayOutputStream
import java.io.InputStream
@ -116,6 +117,36 @@ class AttachmentsClassLoaderTests {
}
}
@Test
fun `Overlapping rules for META-INF serializationwhitelist files`() {
val att1 = importAttachment(fakeAttachment("meta-inf/services/net.corda.core.serialization.serializationwhitelist", "some data").inputStream(), "app", "file1.jar")
val att2 = importAttachment(fakeAttachment("meta-inf/services/net.corda.core.serialization.serializationwhitelist", "some other data").inputStream(), "app", "file2.jar")
AttachmentsClassLoader(arrayOf(att1, att2).map { storage.openAttachment(it)!! })
}
@Ignore("Enable once `requireNoDuplicates` is fixed. The check is currently skipped due to the incorrect logic.")
@Test
fun `Overlapping rules for META-INF random service files`() {
val att1 = importAttachment(fakeAttachment("meta-inf/services/com.example.something", "some data").inputStream(), "app", "file1.jar")
val att2 = importAttachment(fakeAttachment("meta-inf/services/com.example.something", "some other data").inputStream(), "app", "file2.jar")
assertFailsWith(TransactionVerificationException.OverlappingAttachmentsException::class) {
AttachmentsClassLoader(arrayOf(att1, att2).map { storage.openAttachment(it)!! })
}
}
@Ignore("Added test that was removed when the hash-2-signature constraint was added. Enable once `requireNoDuplicates` is fixed.")
@Test
fun `Test overlapping file exception`() {
val att1 = storage.importAttachment(fakeAttachment("file1.txt", "some data").inputStream(), "app", "file1.jar")
val att2 = storage.importAttachment(fakeAttachment("file1.txt", "some other data").inputStream(), "app", "file2.jar")
assertFailsWith(TransactionVerificationException.OverlappingAttachmentsException::class) {
AttachmentsClassLoader(arrayOf(att1, att2).map { storage.openAttachment(it)!! })
}
}
@Test
fun `Check platform independent path handling in attachment jars`() {
val att1 = importAttachment(fakeAttachment("/folder1/foldera/file1.txt", "some data").inputStream(), "app", "file1.jar")