diff --git a/jacodb-taint-configuration/src/main/kotlin/org/jacodb/taint/configuration/TaintConfigurationFeature.kt b/jacodb-taint-configuration/src/main/kotlin/org/jacodb/taint/configuration/TaintConfigurationFeature.kt index 55fef2c0d..1412322bb 100644 --- a/jacodb-taint-configuration/src/main/kotlin/org/jacodb/taint/configuration/TaintConfigurationFeature.kt +++ b/jacodb-taint-configuration/src/main/kotlin/org/jacodb/taint/configuration/TaintConfigurationFeature.kt @@ -16,7 +16,6 @@ package org.jacodb.taint.configuration -import kotlinx.coroutines.runBlocking import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json import kotlinx.serialization.modules.SerializersModule @@ -24,7 +23,6 @@ import kotlinx.serialization.modules.polymorphic import kotlinx.serialization.modules.subclass import org.jacodb.api.* import org.jacodb.api.ext.* -import org.jacodb.impl.features.hierarchyExt import java.nio.file.Path import kotlin.io.path.readText @@ -295,31 +293,49 @@ class TaintConfigurationFeature private constructor( return mkOr(types.flatMap { type -> position.map { TypeMatches(it, type) } }) } - typeMatcher as ClassMatcher + val typeMatchers = (typeMatcher as ClassMatcher).extractAlternatives() + val unresolvedMatchers = mutableListOf() + val disjuncts = mutableListOf() - val pkgMatcher = typeMatcher.pkg - val clsMatcher = typeMatcher.classNameMatcher val cp = method.enclosingClass.classpath - if (pkgMatcher is NameExactMatcher && clsMatcher is NameExactMatcher) { + for (matcher in typeMatchers) { + val pkgMatcher = matcher.pkg + val clsMatcher = matcher.classNameMatcher + + if (pkgMatcher !is NameExactMatcher || clsMatcher !is NameExactMatcher) { + unresolvedMatchers += matcher + continue + } + val type = cp.findTypeOrNull("${pkgMatcher.name}$DOT_DELIMITER${clsMatcher.name}") - ?: return mkOr(emptyList()) - return mkOr(position.map { TypeMatches(it, type) }) - } + ?: continue - val alternatives = typeMatcher.extractAlternatives() - val disjuncts = mutableListOf() + position.mapTo(disjuncts) { TypeMatches(it, type) } + } - alternatives.forEach { classMatcher -> - val allClasses = runBlocking { - cp.hierarchyExt().findSubClasses(cp.objectClass, allHierarchy = true, includeOwn = true) + if (unresolvedMatchers.isNotEmpty()) { + val allClassNames = cp.registeredLocations.flatMapTo(hashSetOf()) { + val names = it.jcLocation?.classNames ?: return@flatMapTo emptyList() + names.map { name -> + val packageName = name.substringBeforeLast(DOT_DELIMITER, missingDelimiterValue = "") + val simpleName = name.substringAfterLast(DOT_DELIMITER) + packageName to simpleName + } } - val types = allClasses.filter { - matches(classMatcher.pkg, it.packageName) && matches(classMatcher.classNameMatcher, it.simpleName) - } + unresolvedMatchers.forEach { classMatcher -> + val matchedClassNames = allClassNames.filter { (packageName, simpleName) -> + matches(classMatcher.pkg, packageName) && matches(classMatcher.classNameMatcher, simpleName) + } - disjuncts += types.flatMap { type -> position.map { TypeMatches(it, type.toType()) } }.toList() + matchedClassNames.flatMapTo(disjuncts) { (packageName, simpleName) -> + val type = cp.findTypeOrNull("${packageName}$DOT_DELIMITER${simpleName}") + ?: return@flatMapTo emptyList() + + position.map { TypeMatches(it, type) } + } + } } return mkOr(disjuncts) diff --git a/jacodb-taint-configuration/src/test/kotlin/org/jacodb/taint/configuration/ConfigurationTest.kt b/jacodb-taint-configuration/src/test/kotlin/org/jacodb/taint/configuration/ConfigurationTest.kt index 9d4687866..f3499c7e9 100644 --- a/jacodb-taint-configuration/src/test/kotlin/org/jacodb/taint/configuration/ConfigurationTest.kt +++ b/jacodb-taint-configuration/src/test/kotlin/org/jacodb/taint/configuration/ConfigurationTest.kt @@ -120,4 +120,15 @@ class ConfigurationTest : BaseTest() { assertTrue(rules.singleOrNull() != null) } + + @Test + fun testIsTypeMatcher() { + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") + val method = cp.findClass>().methods.single { + it.name == "removeAll" && it.parameters.size == 1 + } + val rules = taintFeature.getConfigForMethod(method) + + assertTrue(rules.singleOrNull() != null) + } } diff --git a/jacodb-taint-configuration/src/test/resources/testJsonConfig.json b/jacodb-taint-configuration/src/test/resources/testJsonConfig.json index 5f5485ca2..c44c5531d 100644 --- a/jacodb-taint-configuration/src/test/resources/testJsonConfig.json +++ b/jacodb-taint-configuration/src/test/resources/testJsonConfig.json @@ -479,5 +479,56 @@ } ] } + }, + { + "_": "MethodSink", + "ruleNote": "Test rule for isType matcher", + "cwe": [ + -1 + ], + "methodInfo": { + "cls": { + "packageMatcher": { + "_": "NameIsEqualTo", + "name": "java.util" + }, + "classNameMatcher": { + "_": "NameIsEqualTo", + "name": "List" + } + }, + "functionName": { + "_": "NameIsEqualTo", + "name": "removeAll" + }, + "parametersMatchers": [ + ], + "returnTypeMatcher": { + "_": "AnyTypeMatches" + }, + "applyToOverrides": false, + "functionLabel": null, + "modifier": -1, + "exclude": [ + ] + }, + "condition": { + "_": "IsType", + "position": { + "_": "Argument", + "number": 0 + }, + "type": { + "_": "ClassMatcher", + "packageMatcher": { + "_": "NameMatches", + "pattern": "java\\..*" + }, + "classNameMatcher": { + "_": "NameMatches", + "pattern": "List.*" + } + } + } } ] \ No newline at end of file