diff --git a/gradle.properties b/gradle.properties index 3a28aaf..97b00b0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,6 +2,6 @@ seqraOrg=seqra seqraBuildVersion=2025.10.01.6b66511 -seqraIrVersion=2026.01.21.17b5bbb -seqraConfigVersion=2026.01.21.57ada3f -seqraUtilVersion=2026.01.21.d61d52a +seqraIrVersion=2026.02.01.c54b6cc +seqraConfigVersion=2026.02.01.02e636c +seqraUtilVersion=2026.02.01.44bb600 diff --git a/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/JIRMarkAwareConditionRewriter.kt b/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/JIRMarkAwareConditionRewriter.kt index a8c02c1..2d18675 100644 --- a/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/JIRMarkAwareConditionRewriter.kt +++ b/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/JIRMarkAwareConditionRewriter.kt @@ -7,17 +7,20 @@ import org.seqra.dataflow.configuration.jvm.Not import org.seqra.dataflow.configuration.jvm.Or import org.seqra.dataflow.configuration.jvm.PositionResolver import org.seqra.dataflow.jvm.ap.ifds.JIRMarkAwareConditionExpr.Literal +import org.seqra.dataflow.jvm.ap.ifds.analysis.JIRMethodAnalysisContext import org.seqra.dataflow.jvm.ap.ifds.taint.ContainsMarkOnAnyField import org.seqra.dataflow.jvm.ap.ifds.taint.JIRBasicAtomEvaluator +import org.seqra.ir.api.common.cfg.CommonInst import org.seqra.ir.api.jvm.cfg.JIRValue import org.seqra.util.Maybe class JIRMarkAwareConditionRewriter( positionResolver: PositionResolver>, - typeChecker: JIRFactTypeChecker, + analysisContext: JIRMethodAnalysisContext, + statement: CommonInst, ) { - private val positiveAtomEvaluator = JIRBasicAtomEvaluator(negated = false, positionResolver, typeChecker) - private val negativeAtomEvaluator = JIRBasicAtomEvaluator(negated = true, positionResolver, typeChecker) + private val positiveAtomEvaluator = JIRBasicAtomEvaluator(negated = false, positionResolver, analysisContext, statement) + private val negativeAtomEvaluator = JIRBasicAtomEvaluator(negated = true, positionResolver, analysisContext, statement) fun rewrite(condition: Condition): ExprOrConstant = rewriteCondition(condition) diff --git a/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/alias/JIRIntraProcAliasAnalysis.kt b/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/alias/JIRIntraProcAliasAnalysis.kt index 250976b..ac25f0d 100644 --- a/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/alias/JIRIntraProcAliasAnalysis.kt +++ b/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/alias/JIRIntraProcAliasAnalysis.kt @@ -19,6 +19,7 @@ import org.seqra.dataflow.jvm.ap.ifds.alias.RefValue.Local import org.seqra.ir.api.common.CommonMethod import org.seqra.ir.api.common.cfg.CommonInst import org.seqra.ir.api.jvm.cfg.JIRInst +import org.seqra.ir.api.jvm.cfg.JIRStringConstant import org.seqra.jvm.graph.JApplicationGraph import org.seqra.util.analysis.ApplicationGraph @@ -110,8 +111,13 @@ class JIRIntraProcAliasAnalysis( is RefValue.This -> AccessPathBase.This is RefValue.Static -> AccessPathBase.ClassStatic(loc.type) } + is LocalAlias.Alloc -> { + val assign = cur.stmt as? Stmt.Assign + val const = assign?.expr as? SimpleValue.RefConst + val stringConst = const?.expr as? JIRStringConstant + stringConst?.let { AccessPathBase.Constant("java.lang.String", it.value) } + } is CallReturn, - is LocalAlias.Alloc, is Unknown -> null is HeapAlias -> error("unreachable") } diff --git a/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/analysis/JIRMethodCallFlowFunction.kt b/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/analysis/JIRMethodCallFlowFunction.kt index a8dca45..3831458 100644 --- a/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/analysis/JIRMethodCallFlowFunction.kt +++ b/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/analysis/JIRMethodCallFlowFunction.kt @@ -66,7 +66,7 @@ class JIRMethodCallFlowFunction( override fun propagateZeroToZero() = buildSet { val conditionRewriter = JIRMarkAwareConditionRewriter( CallPositionToJIRValueResolver(callExpr, returnValue), - analysisContext.factTypeChecker + analysisContext, statement ) applySinkRules( @@ -197,7 +197,7 @@ class JIRMethodCallFlowFunction( val conditionRewriter = JIRMarkAwareConditionRewriter( CallPositionToJIRValueResolver(callExpr, returnValue), - analysisContext.factTypeChecker + analysisContext, statement ) val factReader = FinalFactReader(factAp, apManager) @@ -270,7 +270,8 @@ class JIRMethodCallFlowFunction( val simpleConditionEvaluator = JIRSimpleFactAwareConditionEvaluator(conditionRewriter, conditionEvaluator) - val cleaner = TaintCleanActionEvaluator() + val typeResolver = JIRMethodPositionBaseTypeResolver(method) + val cleaner = TaintCleanActionEvaluator(typeResolver) val factReaderBeforeCleaner = FinalFactReader(callerFact, apManager) val cleanerResults = applyCleaner( diff --git a/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/analysis/JIRMethodCallRuleBasedSummaryRewriter.kt b/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/analysis/JIRMethodCallRuleBasedSummaryRewriter.kt index 5c44701..ef85ff6 100644 --- a/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/analysis/JIRMethodCallRuleBasedSummaryRewriter.kt +++ b/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/analysis/JIRMethodCallRuleBasedSummaryRewriter.kt @@ -8,6 +8,7 @@ import org.seqra.dataflow.configuration.jvm.TaintConfigurationItem import org.seqra.dataflow.configuration.jvm.TaintMark import org.seqra.dataflow.jvm.ap.ifds.CallPositionToJIRValueResolver import org.seqra.dataflow.jvm.ap.ifds.JIRMarkAwareConditionRewriter +import org.seqra.dataflow.jvm.ap.ifds.JIRMethodPositionBaseTypeResolver import org.seqra.dataflow.jvm.ap.ifds.TaintConfigUtils.applyCleanerActions import org.seqra.dataflow.jvm.ap.ifds.taint.EvaluatedCleanAction import org.seqra.dataflow.jvm.ap.ifds.taint.FinalFactReader @@ -35,7 +36,7 @@ class JIRMethodCallRuleBasedSummaryRewriter( JIRMarkAwareConditionRewriter( CallPositionToJIRValueResolver(callExpr, returnValue), - analysisContext.factTypeChecker + analysisContext, statement ) } @@ -75,7 +76,8 @@ class JIRMethodCallRuleBasedSummaryRewriter( fun rewriteSummaryFact(fact: FinalFactAp): List> { val startFactReader = FinalFactReader(fact, apManager) - val cleanEvaluator = TaintCleanActionEvaluator() + val typeResolver = JIRMethodPositionBaseTypeResolver(callExpr.method.method) + val cleanEvaluator = TaintCleanActionEvaluator(typeResolver) val cleanedFact = userRuleDefinedActions.applyCleanerActions( evaluator = cleanEvaluator, diff --git a/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/analysis/JIRMethodSequentFlowFunction.kt b/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/analysis/JIRMethodSequentFlowFunction.kt index 4098758..5e1b8aa 100644 --- a/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/analysis/JIRMethodSequentFlowFunction.kt +++ b/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/analysis/JIRMethodSequentFlowFunction.kt @@ -511,7 +511,7 @@ class JIRMethodSequentFlowFunction( val valueResolver = CalleePositionToJIRValueResolver(currentInst.location.method) val conditionRewriter = JIRMarkAwareConditionRewriter( valueResolver, - analysisContext.factTypeChecker + analysisContext, currentInst ) val sourceEvaluator = TaintSourceActionEvaluator( @@ -604,7 +604,7 @@ class JIRMethodSequentFlowFunction( val valueResolver = CalleePositionToJIRValueResolver(currentInst.location.method) val conditionRewriter = JIRMarkAwareConditionRewriter( valueResolver, - analysisContext.factTypeChecker + analysisContext, currentInst ) val exclusion = fact?.exclusions ?: ExclusionSet.Universe diff --git a/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/analysis/JIRMethodStartFlowFunction.kt b/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/analysis/JIRMethodStartFlowFunction.kt index d988372..8e5c758 100644 --- a/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/analysis/JIRMethodStartFlowFunction.kt +++ b/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/analysis/JIRMethodStartFlowFunction.kt @@ -33,7 +33,7 @@ class JIRMethodStartFlowFunction( val method = context.methodEntryPoint.method as JIRMethod val valueResolver = CalleePositionToJIRValueResolver(method) val conditionRewriter = JIRMarkAwareConditionRewriter( - valueResolver, context.factTypeChecker + valueResolver, context, context.methodEntryPoint.statement ) val conditionEvaluator = JIRSimpleFactAwareConditionEvaluator(conditionRewriter, evaluator = null) @@ -93,7 +93,7 @@ class JIRMethodStartFlowFunction( val valueResolver = CalleePositionToJIRValueResolver(method as JIRMethod) val conditionRewriter = JIRMarkAwareConditionRewriter( valueResolver, - context.factTypeChecker + context, context.methodEntryPoint.statement ) val conditionEvaluator = JIRSimpleFactAwareConditionEvaluator(conditionRewriter, evaluator = null) diff --git a/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/taint/JIRBasicAtomEvaluator.kt b/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/taint/JIRBasicAtomEvaluator.kt index 356836f..023c04c 100644 --- a/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/taint/JIRBasicAtomEvaluator.kt +++ b/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/taint/JIRBasicAtomEvaluator.kt @@ -1,5 +1,6 @@ package org.seqra.dataflow.jvm.ap.ifds.taint +import org.seqra.dataflow.ap.ifds.AccessPathBase import org.seqra.dataflow.configuration.jvm.And import org.seqra.dataflow.configuration.jvm.ConditionNameMatcher import org.seqra.dataflow.configuration.jvm.ConditionVisitor @@ -20,12 +21,14 @@ import org.seqra.dataflow.configuration.jvm.Or import org.seqra.dataflow.configuration.jvm.PositionResolver import org.seqra.dataflow.configuration.jvm.TypeMatches import org.seqra.dataflow.configuration.jvm.TypeMatchesPattern -import org.seqra.dataflow.jvm.ap.ifds.JIRFactTypeChecker +import org.seqra.dataflow.jvm.ap.ifds.analysis.JIRMethodAnalysisContext +import org.seqra.ir.api.common.cfg.CommonInst import org.seqra.ir.api.common.cfg.CommonValue import org.seqra.ir.api.jvm.JIRRefType import org.seqra.ir.api.jvm.cfg.JIRBool import org.seqra.ir.api.jvm.cfg.JIRConstant import org.seqra.ir.api.jvm.cfg.JIRInt +import org.seqra.ir.api.jvm.cfg.JIRLocalVar import org.seqra.ir.api.jvm.cfg.JIRNullConstant import org.seqra.ir.api.jvm.cfg.JIRStringConstant import org.seqra.ir.api.jvm.cfg.JIRValue @@ -36,8 +39,11 @@ import org.seqra.util.onSome class JIRBasicAtomEvaluator( private val negated: Boolean, private val positionResolver: PositionResolver>, - private val typeChecker: JIRFactTypeChecker + private val analysisContext: JIRMethodAnalysisContext, + private val statement: CommonInst, ) : ConditionVisitor { + private val typeChecker get() = analysisContext.factTypeChecker + override fun visit(condition: Not): Boolean = error("Non-atomic condition") override fun visit(condition: And): Boolean = error("Non-atomic condition") override fun visit(condition: Or): Boolean = error("Non-atomic condition") @@ -159,8 +165,24 @@ class JIRBasicAtomEvaluator( } private fun matches(value: JIRValue, pattern: Regex): Boolean { - if (value !is JIRStringConstant) return false - return pattern.matches(value.value) + if (value is JIRStringConstant) { + return pattern.matches(value.value) + } + + if (value is JIRLocalVar) { + val base = AccessPathBase.LocalVar(value.index) + val aliasInfo = analysisContext.aliasAnalysis?.findAlias(base, statement) + val constants = aliasInfo + ?.mapNotNull { ai -> ai.base.takeIf { ai.accessors.isEmpty() } } + ?.filterIsInstance() + .orEmpty() + + if (constants.isNotEmpty()) { + return constants.any { pattern.matches(it.value) } + } + } + + return false } private fun typeMatches(value: CommonValue, condition: TypeMatches): Boolean { diff --git a/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/taint/TaintEvaluator.kt b/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/taint/TaintEvaluator.kt index c7204e4..084eb36 100644 --- a/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/taint/TaintEvaluator.kt +++ b/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/taint/TaintEvaluator.kt @@ -143,7 +143,9 @@ data class EvaluatedCleanAction( } } -class TaintCleanActionEvaluator { +class TaintCleanActionEvaluator( + private val positionTypeResolver: PositionResolver, +) { fun evaluate( initialFact: EvaluatedCleanAction, rule: TaintConfigurationItem, @@ -159,7 +161,17 @@ class TaintCleanActionEvaluator { action: RemoveMark, ): List { val variable = action.position.resolveAp() - return removeFinalFact(initialFact, variable, action.mark, rule, action) + val cleaned = removeFinalFact(initialFact, variable, action.mark, rule, action) + + val positionType = positionTypeResolver.resolve(action.position) + if (positionType?.typeName != STRING) { + return cleaned + } + + val stringBytesVar = PositionWithAccess(action.position, stringBytes).resolveAp() + return cleaned.flatMap { f -> + removeFinalFact(f, stringBytesVar, action.mark, rule, action) + } } private fun removeAllFacts( @@ -255,6 +267,13 @@ class TaintCleanActionEvaluator { companion object { private val logger = KotlinLogging.logger {} + + private const val STRING = "java.lang.String" + + // todo: fix in config? + // string bytes virtual field fully reflects the string content. + // So, if we clean string, we should clean its byte content + private val stringBytes = PositionAccessor.FieldAccessor(STRING, "", "byte[]") } } diff --git a/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/trace/JIRMethodCallPrecondition.kt b/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/trace/JIRMethodCallPrecondition.kt index cb03f6e..a4b7acb 100644 --- a/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/trace/JIRMethodCallPrecondition.kt +++ b/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/trace/JIRMethodCallPrecondition.kt @@ -108,7 +108,7 @@ class JIRMethodCallPrecondition( val conditionRewriter = JIRMarkAwareConditionRewriter( jIRValueResolver, - analysisContext.factTypeChecker + analysisContext, statement ) for (rule in taintConfig.sourceRulesForMethod(method, statement, fact = null)) { @@ -144,7 +144,7 @@ class JIRMethodCallPrecondition( val conditionRewriter = JIRMarkAwareConditionRewriter( jIRValueResolver, - analysisContext.factTypeChecker + analysisContext, statement ) for (rule in passRules) { diff --git a/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/trace/JIRMethodSequentPrecondition.kt b/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/trace/JIRMethodSequentPrecondition.kt index 807ab43..beeaa79 100644 --- a/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/trace/JIRMethodSequentPrecondition.kt +++ b/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/trace/JIRMethodSequentPrecondition.kt @@ -256,7 +256,7 @@ class JIRMethodSequentPrecondition( val valueResolver = CalleePositionToJIRValueResolver(currentInst.location.method) val conditionRewriter = JIRMarkAwareConditionRewriter( - valueResolver, analysisContext.factTypeChecker + valueResolver, analysisContext, currentInst ) for (rule in sourceRules) { diff --git a/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/trace/JIRMethodStartPrecondition.kt b/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/trace/JIRMethodStartPrecondition.kt index 5b91617..f69d1b1 100644 --- a/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/trace/JIRMethodStartPrecondition.kt +++ b/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/trace/JIRMethodStartPrecondition.kt @@ -26,7 +26,7 @@ class JIRMethodStartPrecondition( val valueResolver = CalleePositionToJIRValueResolver(method) val conditionRewriter = JIRMarkAwareConditionRewriter( valueResolver, - context.factTypeChecker + context, context.methodEntryPoint.statement ) val conditionEvaluator = JIRSimpleFactAwareConditionEvaluator(conditionRewriter, evaluator = null) diff --git a/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/util/JIRInstListBuilder.kt b/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/util/JIRInstListBuilder.kt index 34141e1..246ea0e 100644 --- a/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/util/JIRInstListBuilder.kt +++ b/seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/util/JIRInstListBuilder.kt @@ -15,7 +15,7 @@ import kotlin.contracts.InvocationKind import kotlin.contracts.contract class JIRInstListBuilder( - private val mutableInstructions: MutableList = mutableListOf() + val mutableInstructions: MutableList = mutableListOf() ) : JIRInstList { private var localVarIdx: Int