Skip to content

Commit 8b446b0

Browse files
committed
Support user taint analysis config
1 parent ad1a854 commit 8b446b0

File tree

9 files changed

+87
-26
lines changed

9 files changed

+87
-26
lines changed

utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ class UtBotSymbolicEngine(
108108
val mockStrategy: MockStrategy = NO_MOCKS,
109109
chosenClassesToMockAlways: Set<ClassId>,
110110
applicationContext: ApplicationContext,
111-
private val solverTimeoutInMillis: Int = checkSolverTimeoutMillis
111+
private val solverTimeoutInMillis: Int = checkSolverTimeoutMillis,
112+
taintUserConfigPath: String? = null,
112113
) : UtContextInitializer() {
113114
private val graph = methodUnderTest.sootMethod.jimpleBody().apply {
114115
logger.trace { "JIMPLE for $methodUnderTest:\n$this" }
@@ -144,7 +145,10 @@ class UtBotSymbolicEngine(
144145
private val taintMarkRegistry: TaintMarkRegistry = TaintMarkRegistry()
145146
private val taintMarkManager: TaintMarkManager = TaintMarkManager(taintMarkRegistry)
146147

147-
private val taintConfigProvider: TaintConfigurationProvider = TaintConfigurationProviderResources("taint/config.yaml")
148+
private val taintConfigProvider: TaintConfigurationProvider = TaintConfigurationProviderCombiner(listOf(
149+
TaintConfigurationProviderUserRules(taintUserConfigPath ?: ""),
150+
TaintConfigurationProviderResources("taint/config.yaml")
151+
))
148152
private val taintConfiguration: TaintConfiguration = taintConfigProvider.getConfiguration()
149153

150154
private val taintContext: TaintContext = TaintContext(taintMarkManager, taintConfiguration)

utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ open class TestCaseGenerator(
135135
mockStrategy: MockStrategyApi,
136136
chosenClassesToMockAlways: Set<ClassId> = Mocker.javaDefaultClasses.mapTo(mutableSetOf()) { it.id },
137137
methodsGenerationTimeout: Long = utBotGenerationTimeoutInMillis,
138+
taintConfigPath: String? = null,
138139
generate: (engine: UtBotSymbolicEngine) -> Flow<UtResult> = defaultTestFlow(methodsGenerationTimeout)
139140
): List<UtMethodTestSet> {
140141
if (isCanceled()) return methods.map { UtMethodTestSet(it) }
@@ -168,7 +169,8 @@ open class TestCaseGenerator(
168169
mockStrategy,
169170
chosenClassesToMockAlways,
170171
applicationContext,
171-
executionTimeEstimator
172+
executionTimeEstimator,
173+
taintConfigPath
172174
)
173175

174176
engineActions.map { engine.apply(it) }
@@ -258,7 +260,8 @@ open class TestCaseGenerator(
258260
mockStrategyApi: MockStrategyApi,
259261
chosenClassesToMockAlways: Set<ClassId>,
260262
applicationContext: ApplicationContext,
261-
executionTimeEstimator: ExecutionTimeEstimator
263+
executionTimeEstimator: ExecutionTimeEstimator,
264+
taintConfigPath: String? = null,
262265
): UtBotSymbolicEngine {
263266
logger.debug("Starting symbolic execution for $method --$mockStrategyApi--")
264267
return UtBotSymbolicEngine(
@@ -269,7 +272,8 @@ open class TestCaseGenerator(
269272
mockStrategy = mockStrategyApi.toModel(),
270273
chosenClassesToMockAlways = chosenClassesToMockAlways,
271274
applicationContext = applicationContext,
272-
solverTimeoutInMillis = executionTimeEstimator.updatedSolverCheckTimeoutMillis
275+
solverTimeoutInMillis = executionTimeEstimator.updatedSolverCheckTimeoutMillis,
276+
taintUserConfigPath = taintConfigPath,
273277
)
274278
}
275279

utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessMain.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch
113113
MockStrategyApi.valueOf(params.mockStrategy),
114114
kryoHelper.readObject(params.chosenClassesToMockAlways),
115115
params.timeout,
116+
params.taintConfigPath,
116117
generate = generateFlow,
117118
)
118119
.summarizeAll(Paths.get(params.searchDirectory), null)

utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ class EngineProcessModel private constructor(
7272
}
7373

7474

75-
const val serializationHash = -120710112541549600L
75+
const val serializationHash = 1663132864271860294L
7676

7777
}
7878
override val serializersOwner: ISerializersOwner get() = EngineProcessModel
@@ -173,7 +173,7 @@ val IProtocol.engineProcessModel get() = getOrCreateExtension(EngineProcessModel
173173

174174

175175
/**
176-
* #### Generated from [EngineProcessModel.kt:98]
176+
* #### Generated from [EngineProcessModel.kt:100]
177177
*/
178178
data class FindMethodParamNamesArguments (
179179
val classId: ByteArray,
@@ -236,7 +236,7 @@ data class FindMethodParamNamesArguments (
236236

237237

238238
/**
239-
* #### Generated from [EngineProcessModel.kt:102]
239+
* #### Generated from [EngineProcessModel.kt:104]
240240
*/
241241
data class FindMethodParamNamesResult (
242242
val paramNames: ByteArray
@@ -293,7 +293,7 @@ data class FindMethodParamNamesResult (
293293

294294

295295
/**
296-
* #### Generated from [EngineProcessModel.kt:91]
296+
* #### Generated from [EngineProcessModel.kt:93]
297297
*/
298298
data class FindMethodsInClassMatchingSelectedArguments (
299299
val classId: ByteArray,
@@ -356,7 +356,7 @@ data class FindMethodsInClassMatchingSelectedArguments (
356356

357357

358358
/**
359-
* #### Generated from [EngineProcessModel.kt:95]
359+
* #### Generated from [EngineProcessModel.kt:97]
360360
*/
361361
data class FindMethodsInClassMatchingSelectedResult (
362362
val executableIds: ByteArray
@@ -424,7 +424,8 @@ data class GenerateParams (
424424
val isSymbolicEngineEnabled: Boolean,
425425
val isFuzzingEnabled: Boolean,
426426
val fuzzingValue: Double,
427-
val searchDirectory: String
427+
val searchDirectory: String,
428+
val taintConfigPath: String?
428429
) : IPrintable {
429430
//companion
430431

@@ -442,7 +443,8 @@ data class GenerateParams (
442443
val isFuzzingEnabled = buffer.readBool()
443444
val fuzzingValue = buffer.readDouble()
444445
val searchDirectory = buffer.readString()
445-
return GenerateParams(methods, mockStrategy, chosenClassesToMockAlways, timeout, generationTimeout, isSymbolicEngineEnabled, isFuzzingEnabled, fuzzingValue, searchDirectory)
446+
val taintConfigPath = buffer.readNullable { buffer.readString() }
447+
return GenerateParams(methods, mockStrategy, chosenClassesToMockAlways, timeout, generationTimeout, isSymbolicEngineEnabled, isFuzzingEnabled, fuzzingValue, searchDirectory, taintConfigPath)
446448
}
447449

448450
override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: GenerateParams) {
@@ -455,6 +457,7 @@ data class GenerateParams (
455457
buffer.writeBool(value.isFuzzingEnabled)
456458
buffer.writeDouble(value.fuzzingValue)
457459
buffer.writeString(value.searchDirectory)
460+
buffer.writeNullable(value.taintConfigPath) { buffer.writeString(it) }
458461
}
459462

460463

@@ -479,6 +482,7 @@ data class GenerateParams (
479482
if (isFuzzingEnabled != other.isFuzzingEnabled) return false
480483
if (fuzzingValue != other.fuzzingValue) return false
481484
if (searchDirectory != other.searchDirectory) return false
485+
if (taintConfigPath != other.taintConfigPath) return false
482486

483487
return true
484488
}
@@ -494,6 +498,7 @@ data class GenerateParams (
494498
__r = __r*31 + isFuzzingEnabled.hashCode()
495499
__r = __r*31 + fuzzingValue.hashCode()
496500
__r = __r*31 + searchDirectory.hashCode()
501+
__r = __r*31 + if (taintConfigPath != null) taintConfigPath.hashCode() else 0
497502
return __r
498503
}
499504
//pretty print
@@ -509,6 +514,7 @@ data class GenerateParams (
509514
print("isFuzzingEnabled = "); isFuzzingEnabled.print(printer); println()
510515
print("fuzzingValue = "); fuzzingValue.print(printer); println()
511516
print("searchDirectory = "); searchDirectory.print(printer); println()
517+
print("taintConfigPath = "); taintConfigPath.print(printer); println()
512518
}
513519
printer.print(")")
514520
}
@@ -518,7 +524,7 @@ data class GenerateParams (
518524

519525

520526
/**
521-
* #### Generated from [EngineProcessModel.kt:58]
527+
* #### Generated from [EngineProcessModel.kt:60]
522528
*/
523529
data class GenerateResult (
524530
val notEmptyCases: Int,
@@ -581,7 +587,7 @@ data class GenerateResult (
581587

582588

583589
/**
584-
* #### Generated from [EngineProcessModel.kt:110]
590+
* #### Generated from [EngineProcessModel.kt:112]
585591
*/
586592
data class GenerateTestReportArgs (
587593
val eventLogMessage: String?,
@@ -674,7 +680,7 @@ data class GenerateTestReportArgs (
674680

675681

676682
/**
677-
* #### Generated from [EngineProcessModel.kt:119]
683+
* #### Generated from [EngineProcessModel.kt:121]
678684
*/
679685
data class GenerateTestReportResult (
680686
val notifyMessage: String,
@@ -806,7 +812,7 @@ data class JdkInfo (
806812

807813

808814
/**
809-
* #### Generated from [EngineProcessModel.kt:86]
815+
* #### Generated from [EngineProcessModel.kt:88]
810816
*/
811817
data class MethodDescription (
812818
val name: String,
@@ -875,7 +881,7 @@ data class MethodDescription (
875881

876882

877883
/**
878-
* #### Generated from [EngineProcessModel.kt:62]
884+
* #### Generated from [EngineProcessModel.kt:64]
879885
*/
880886
data class RenderParams (
881887
val testSetsId: Long,
@@ -1016,7 +1022,7 @@ data class RenderParams (
10161022

10171023

10181024
/**
1019-
* #### Generated from [EngineProcessModel.kt:79]
1025+
* #### Generated from [EngineProcessModel.kt:81]
10201026
*/
10211027
data class RenderResult (
10221028
val generatedCode: String,
@@ -1079,7 +1085,7 @@ data class RenderResult (
10791085

10801086

10811087
/**
1082-
* #### Generated from [EngineProcessModel.kt:83]
1088+
* #### Generated from [EngineProcessModel.kt:85]
10831089
*/
10841090
data class SetupContextParams (
10851091
val classpathForUrlsClassloader: List<String>
@@ -1217,7 +1223,7 @@ data class TestGeneratorParams (
12171223

12181224

12191225
/**
1220-
* #### Generated from [EngineProcessModel.kt:105]
1226+
* #### Generated from [EngineProcessModel.kt:107]
12211227
*/
12221228
data class WriteSarifReportArguments (
12231229
val testSetsId: Long,

utbot-framework/src/main/kotlin/org/utbot/taint/TaintConfigurationProvider.kt

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.utbot.taint
22

3+
import org.utbot.common.PathUtil.toPath
34
import org.utbot.taint.model.TaintConfiguration
45
import org.utbot.taint.parser.TaintYamlParser
56

@@ -16,7 +17,6 @@ interface TaintConfigurationProvider {
1617
* @param configPath relative path to the .yaml file in resources
1718
*/
1819
class TaintConfigurationProviderResources(private val configPath: String) : TaintConfigurationProvider {
19-
2020
override fun getConfiguration(): TaintConfiguration {
2121
val configUrl = javaClass.classLoader.getResource(configPath)
2222
?: return TaintConfiguration()
@@ -26,6 +26,28 @@ class TaintConfigurationProviderResources(private val configPath: String) : Tain
2626
}
2727
}
2828

29+
/**
30+
* Reads and parses [TaintConfiguration] from the [configPath].
31+
*
32+
* @param configPath relative path to the .yaml file in the user's project.
33+
*/
34+
class TaintConfigurationProviderUserRules(private val configPath: String) : TaintConfigurationProvider {
35+
override fun getConfiguration(): TaintConfiguration {
36+
val configFile = configPath.toPath().toFile()
37+
if (!configFile.exists()) {
38+
return TaintConfiguration()
39+
}
40+
val yamlInput = configFile.readText()
41+
val dtoConfig = TaintYamlParser.parse(yamlInput)
42+
return TaintConfigurationAdapter.convert(dtoConfig)
43+
}
44+
}
45+
46+
class TaintConfigurationProviderCombiner(private val inners: List<TaintConfigurationProvider>) : TaintConfigurationProvider {
47+
override fun getConfiguration(): TaintConfiguration =
48+
inners.fold(TaintConfiguration()) { resultConfig, configProvider ->
49+
resultConfig + configProvider.getConfiguration()
50+
}
51+
}
52+
2953
// TODO: TaintConfigurationProviderCached -- stores binary config to the temp directory
30-
// TODO: TaintConfigurationProviderUserRules -- from .utbot/taint-config.yaml
31-
// TODO: TaintConfigurationProviderCombiner -- takes several providers and combines their results

utbot-framework/src/main/kotlin/org/utbot/taint/model/TaintConfiguration.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ data class TaintConfiguration(
3333
equalParameters(executableId, it.signature) && equalMethodNames(executableId, it.methodFqn)
3434
}
3535

36+
operator fun plus(other: TaintConfiguration): TaintConfiguration =
37+
TaintConfiguration(
38+
sources = sources + other.sources,
39+
passes = passes + other.passes,
40+
cleaners = cleaners + other.cleaners,
41+
sinks = sinks + other.sinks
42+
)
43+
3644
// internal
3745

3846
private fun equalParameters(executableId: ExecutableId, signature: TaintSignature): Boolean =

utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import com.intellij.openapi.roots.OrderEnumerator
1212
import com.intellij.openapi.ui.Messages
1313
import com.intellij.openapi.util.Computable
1414
import com.intellij.openapi.util.text.StringUtil
15+
import com.intellij.project.stateStore
1516
import com.intellij.psi.PsiClass
1617
import com.intellij.psi.PsiMethod
1718
import com.intellij.refactoring.util.classMembers.MemberInfo
@@ -268,6 +269,8 @@ object UtTestsDialogProcessor {
268269
}
269270
.executeSynchronously()
270271

272+
val taintConfigPath = getTaintConfigPath(project)
273+
271274
withStaticsSubstitutionRequired(true) {
272275
val startTime = System.currentTimeMillis()
273276
val timerHandler =
@@ -293,7 +296,8 @@ object UtTestsDialogProcessor {
293296
true,
294297
UtSettings.useFuzzing,
295298
project.service<Settings>().fuzzingValue,
296-
searchDirectory.pathString
299+
searchDirectory.pathString,
300+
taintConfigPath?.pathString
297301
)
298302

299303
if (rdGenerateResult.notEmptyCases == 0) {
@@ -347,6 +351,14 @@ object UtTestsDialogProcessor {
347351
}
348352
}
349353

354+
/**
355+
* Returns "{project}/.idea/utbot-taint-config.yaml" or null if it does not exists
356+
*/
357+
private fun getTaintConfigPath(project: Project): Path? {
358+
val path = project.stateStore.directoryStorePath?.resolve("utbot-taint-config.yaml")
359+
return if (path != null && path.toFile().exists()) path else null
360+
}
361+
350362
private val PsiClass.canonicalName: String
351363
/*
352364
This method calculates exactly name that is used by compiler convention,

utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,8 @@ class EngineProcess private constructor(val project: Project, private val classN
256256
isSymbolicEngineEnabled: Boolean,
257257
isFuzzingEnabled: Boolean,
258258
fuzzingValue: Double,
259-
searchDirectory: String
259+
searchDirectory: String,
260+
taintConfigPath: String?
260261
): RdTestGenerationResult {
261262
assertReadAccessNotAllowed()
262263
val params = GenerateParams(
@@ -268,7 +269,8 @@ class EngineProcess private constructor(val project: Project, private val classN
268269
isSymbolicEngineEnabled,
269270
isFuzzingEnabled,
270271
fuzzingValue,
271-
searchDirectory
272+
searchDirectory,
273+
taintConfigPath
272274
)
273275
val result = engineModel.generate.startBlocking(params)
274276

utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ object EngineProcessModel : Ext(EngineProcessRoot) {
5454
field("fuzzingValue", PredefinedType.double)
5555
// method filters
5656
field("searchDirectory", PredefinedType.string)
57+
// taint analysis
58+
field("taintConfigPath", PredefinedType.string.nullable)
5759
}
5860
val generateResult = structdef {
5961
field("notEmptyCases", PredefinedType.int)

0 commit comments

Comments
 (0)