CORDA-3644: Scan the CorDapp classloader directly for SerializationWhitelist. (#6014)

* CORDA-3644: Scan the CorDapp classloader directly for SerializationWhitelist.

* CORDA-3644: Filter CorDapps from out-of-process node classpaths by their manifest attributes. Also exclude directories and blatant test artifacts.

* Fix IRS Demo - its "tests" artifact had a non-standard classifier of "test".
This commit is contained in:
Chris Rankin
2020-03-04 10:09:40 +00:00
committed by GitHub
parent 20c5040826
commit e006b871c8
6 changed files with 86 additions and 38 deletions

View File

@ -1,6 +1,5 @@
package net.corda.node.logging
import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.StartableByRPC
@ -23,7 +22,13 @@ class ErrorCodeLoggingTests {
node.rpc.startFlow(::MyFlow).waitForCompletion()
val logFile = node.logFile()
val linesWithErrorCode = logFile.useLines { lines -> lines.filter { line -> line.contains("[errorCode=") }.filter { line -> line.contains("moreInformationAt=https://errors.corda.net/") }.toList() }
val linesWithErrorCode = logFile.useLines { lines ->
lines.filter { line ->
line.contains("[errorCode=")
}.filter { line ->
line.contains("moreInformationAt=https://errors.corda.net/")
}.toList()
}
assertThat(linesWithErrorCode).isNotEmpty
}
@ -35,10 +40,11 @@ class ErrorCodeLoggingTests {
fun `When logging is set to error level, there are no other levels logged after node startup`() {
driver(DriverParameters(notarySpecs = emptyList())) {
val node = startNode(startInSameProcess = false, logLevelOverride = "ERROR").getOrThrow()
node.rpc.startFlow(::MyFlow).waitForCompletion()
val logFile = node.logFile()
val lengthAfterStart = logFile.length()
node.rpc.startFlow(::MyFlow).waitForCompletion()
// An exception thrown in a flow will log at the "INFO" level.
assertThat(logFile.length()).isEqualTo(0)
assertThat(logFile.length()).isEqualTo(lengthAfterStart)
}
}

View File

@ -22,7 +22,6 @@ import net.corda.node.VersionInfo
import net.corda.nodeapi.internal.cordapp.CordappLoader
import net.corda.nodeapi.internal.coreContractClasses
import net.corda.serialization.internal.DefaultWhitelist
import org.apache.commons.collections4.map.LRUMap
import java.lang.reflect.Modifier
import java.math.BigInteger
import java.net.URL
@ -293,9 +292,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
}
private fun findWhitelists(cordappJarPath: RestrictedURL): List<SerializationWhitelist> {
val whitelists = URLClassLoader(arrayOf(cordappJarPath.url)).use {
ServiceLoader.load(SerializationWhitelist::class.java, it).toList()
}
val whitelists = ServiceLoader.load(SerializationWhitelist::class.java, appClassLoader).toList()
return whitelists.filter {
it.javaClass.location == cordappJarPath.url && it.javaClass.name.startsWith(cordappJarPath.qualifiedNamePrefix)
} + DefaultWhitelist // Always add the DefaultWhitelist to the whitelist for an app.
@ -309,19 +306,21 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
return scanResult.getClassesWithSuperclass(MappedSchema::class).instances().toSet()
}
private val cachedScanResult = LRUMap<RestrictedURL, RestrictedScanResult>(1000)
private fun scanCordapp(cordappJarPath: RestrictedURL): RestrictedScanResult {
logger.info("Scanning CorDapp in ${cordappJarPath.url}")
return cachedScanResult.computeIfAbsent(cordappJarPath) {
val scanResult = ClassGraph().addClassLoader(appClassLoader).overrideClasspath(cordappJarPath.url).enableAllInfo().pooledScan()
RestrictedScanResult(scanResult, cordappJarPath.qualifiedNamePrefix)
}
val cordappElement = cordappJarPath.url.toString()
logger.info("Scanning CorDapp in $cordappElement")
val scanResult = ClassGraph()
.filterClasspathElements { elt -> elt == cordappElement }
.overrideClassLoaders(appClassLoader)
.ignoreParentClassLoaders()
.enableAllInfo()
.pooledScan()
return RestrictedScanResult(scanResult, cordappJarPath.qualifiedNamePrefix)
}
private fun <T : Any> loadClass(className: String, type: KClass<T>): Class<out T>? {
return try {
appClassLoader.loadClass(className).asSubclass(type.java)
Class.forName(className, false, appClassLoader).asSubclass(type.java)
} catch (e: ClassCastException) {
logger.warn("As $className must be a sub-type of ${type.java.name}")
null