mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-04 17:20:55 +07:00
[java-inspections] IDEA-282916 Improve tainted analysis, support local methods
GitOrigin-RevId: 27871a7bbab9f093863d110711f7bb67c9b09fd9
This commit is contained in:
committed by
intellij-monorepo-bot
parent
8579ca0616
commit
4edcb035e7
@@ -2,10 +2,7 @@
|
||||
package com.intellij.codeInspection
|
||||
|
||||
import com.intellij.codeInspection.util.InspectionMessage
|
||||
import org.jetbrains.uast.UAnchorOwner
|
||||
import org.jetbrains.uast.UCallExpression
|
||||
import org.jetbrains.uast.UDeclaration
|
||||
import org.jetbrains.uast.UReferenceExpression
|
||||
import org.jetbrains.uast.*
|
||||
|
||||
@JvmOverloads
|
||||
fun ProblemsHolder.registerUProblem(
|
||||
@@ -49,4 +46,15 @@ fun ProblemsHolder.registerUProblem(
|
||||
) {
|
||||
val anchor = element.referenceNameElement?.sourcePsi ?: return
|
||||
registerProblem(anchor, descriptionTemplate, highlightType, *fixes)
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun ProblemsHolder.registerUProblem(
|
||||
element: UExpression,
|
||||
descriptionTemplate: @InspectionMessage String,
|
||||
vararg fixes: LocalQuickFix,
|
||||
highlightType: ProblemHighlightType = ProblemHighlightType.GENERIC_ERROR_OR_WARNING) {
|
||||
val anchor = element.sourcePsi ?: return
|
||||
if (anchor.textLength == 0) return
|
||||
registerProblem(anchor, descriptionTemplate, highlightType, *fixes)
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
<html><body>
|
||||
Reports cases when non-safe string is passed to a method with parameter marked with <code>@Untainted</code> annotations, returned from annotated methods
|
||||
or assigned to annotated fields, parameters or local variables.
|
||||
or assigned to annotated fields, parameters or local variables. Kotlin `set` and `get` methods for fields are not supported as entry points.
|
||||
<p>
|
||||
Safe object is:
|
||||
Safe object is:
|
||||
<ul>
|
||||
<li>a string literal, interface instance or enum object</li>
|
||||
<li>a result of a call of a method that is marked as <code>@Untainted</code></li>
|
||||
|
||||
@@ -76,6 +76,7 @@ jvm.inspections.source.to.sink.flow.assigned.unsafe=Unsafe string is assigned to
|
||||
jvm.inspections.source.to.sink.flow.assigned.unknown=Unknown string is assigned to safe variable
|
||||
jvm.inspections.source.to.sink.flow.common.unsafe=Unsafe string is used in a safe context
|
||||
jvm.inspections.source.to.sink.flow.common.unknown=Unknown string is used in a safe context
|
||||
jvm.inspections.source.to.sink.flow.too.complex=Too complex to check that the string is safe in a safe context
|
||||
jvm.inspections.source.unsafe.to.sink.flow.mark.as.safe.family=Mark as requiring validation
|
||||
jvm.inspections.source.unsafe.to.sink.flow.mark.as.safe.text=Mark elements as requiring validation
|
||||
jvm.inspections.source.unsafe.to.sink.flow.mark.as.safe.command.name=Mark as Requiring Validation
|
||||
|
||||
@@ -109,7 +109,7 @@ internal class LoggingStringPartEvaluator {
|
||||
val visitor = object : AbstractUastVisitor() {
|
||||
val used: MutableSet<ULocalVariable> = mutableSetOf()
|
||||
override fun visitBinaryExpression(node: UBinaryExpression): Boolean {
|
||||
if (node.operator == UastBinaryOperator.ASSIGN) {
|
||||
if (node.operator is UastBinaryOperator.AssignOperator) {
|
||||
val leftOperand = node.leftOperand
|
||||
if (leftOperand is USimpleNameReferenceExpression) {
|
||||
val resolveToUElement = leftOperand.resolveToUElement()
|
||||
|
||||
@@ -16,10 +16,7 @@ import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.command.undo.BasicUndoableAction;
|
||||
import com.intellij.openapi.command.undo.UndoManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiAnnotation;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiModifierListOwner;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.refactoring.ui.ConflictsDialog;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
@@ -28,9 +25,7 @@ import com.intellij.util.containers.MultiMap;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.uast.UElement;
|
||||
import org.jetbrains.uast.UExpression;
|
||||
import org.jetbrains.uast.UastContextKt;
|
||||
import org.jetbrains.uast.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -78,13 +73,21 @@ class MarkAsSafeFix extends LocalQuickFixOnPsiElement {
|
||||
@Nullable
|
||||
private List<PsiElement> getElementsToMark(@NotNull UExpression uExpression) {
|
||||
TaintAnalyzer taintAnalyzer = new TaintAnalyzer(myTaintValueFactory);
|
||||
TaintValue taintValue = taintAnalyzer.analyzeExpression(uExpression, false);
|
||||
if (taintValue != TaintValue.UNKNOWN) return null;
|
||||
return ContainerUtil.map(taintAnalyzer.getNonMarkedElements(), e -> e.myNonMarked);
|
||||
try {
|
||||
TaintValue taintValue = taintAnalyzer.analyzeExpression(uExpression, false);
|
||||
if (taintValue != TaintValue.UNKNOWN) return null;
|
||||
}
|
||||
catch (DeepTaintAnalyzerException e) {
|
||||
return null;
|
||||
}
|
||||
List<PsiElement> elements = new ArrayList<>(ContainerUtil.map(taintAnalyzer.getNonMarkedElements(), e -> e.myNonMarked));
|
||||
ContainerUtil.removeDuplicates(elements);
|
||||
return elements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull IntentionPreviewInfo generatePreview(@NotNull Project project, @NotNull ProblemDescriptor previewDescriptor) {
|
||||
//noinspection unchecked
|
||||
UExpression uExpression =
|
||||
(UExpression)UastContextKt.getUastParentOfTypes(previewDescriptor.getStartElement(), new Class[]{UExpression.class});
|
||||
|
||||
@@ -335,6 +338,12 @@ class MarkAsSafeFix extends LocalQuickFixOnPsiElement {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
UElement sourceUElement = UastContextKt.toUElement(element);
|
||||
if (element instanceof PsiMethod &&
|
||||
sourceUElement != null &&
|
||||
UastContextKt.toUElement(sourceUElement.getSourcePsi()) instanceof UField uField) {
|
||||
element = uField.getJavaPsi();
|
||||
}
|
||||
JvmModifiersOwner jvmModifiersOwner = ObjectUtils.tryCast(element, JvmModifiersOwner.class);
|
||||
if (jvmModifiersOwner == null) return false;
|
||||
AnnotationRequest request = AnnotationRequestsKt.annotationRequest(annotation);
|
||||
|
||||
@@ -34,9 +34,9 @@ public class PropagateFix extends LocalQuickFixAndIntentionActionOnPsiElement {
|
||||
|
||||
private final boolean supportRefactoring;
|
||||
|
||||
PropagateFix(@NotNull PsiElement sourcePsi,
|
||||
@NotNull TaintValueFactory taintValueFactory,
|
||||
boolean supportRefactoring) {
|
||||
PropagateFix(@NotNull PsiElement sourcePsi,
|
||||
@NotNull TaintValueFactory taintValueFactory,
|
||||
boolean supportRefactoring) {
|
||||
super(sourcePsi);
|
||||
myTaintValueFactory = taintValueFactory;
|
||||
this.supportRefactoring = supportRefactoring;
|
||||
@@ -68,7 +68,13 @@ public class PropagateFix extends LocalQuickFixAndIntentionActionOnPsiElement {
|
||||
PsiElement reportedElement = uExpression.getSourcePsi();
|
||||
if (reportedElement == null) return;
|
||||
TaintAnalyzer analyzer = new TaintAnalyzer(myTaintValueFactory);
|
||||
if (analyzer.analyzeExpression(uExpression, false) != TaintValue.UNKNOWN) return;
|
||||
try {
|
||||
TaintValue value = analyzer.analyzeExpression(uExpression, false);
|
||||
if (value != TaintValue.UNKNOWN) return;
|
||||
}
|
||||
catch (DeepTaintAnalyzerException e) {
|
||||
return;
|
||||
}
|
||||
PsiElement target = ((UResolvable)uExpression).resolve();
|
||||
TaintNode root = new TaintNode(null, target, reportedElement, myTaintValueFactory, true);
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
|
||||
@@ -75,7 +75,7 @@ class SourceToSinkFlowInspection : AbstractBaseUastLocalInspectionTool() {
|
||||
isOnTheFly: Boolean,
|
||||
session: LocalInspectionToolSession): PsiElementVisitor {
|
||||
val scope = GlobalSearchScope.allScope(holder.project)
|
||||
val firstAnnotation: String = untaintedAnnotations.firstOrNull() {
|
||||
val firstAnnotation: String = untaintedAnnotations.firstOrNull {
|
||||
it != null && JavaPsiFacade.getInstance(holder.project).findClass(it, scope) != null
|
||||
} ?: return PsiElementVisitor.EMPTY_VISITOR
|
||||
|
||||
@@ -94,7 +94,9 @@ class SourceToSinkFlowInspection : AbstractBaseUastLocalInspectionTool() {
|
||||
UReturnExpression::class.java,
|
||||
UBinaryExpression::class.java,
|
||||
ULocalVariable::class.java,
|
||||
UField::class.java),
|
||||
UField::class.java,
|
||||
UDeclarationsExpression::class.java,
|
||||
UParameter::class.java),
|
||||
directOnly = true)
|
||||
|
||||
|
||||
@@ -115,6 +117,22 @@ class SourceToSinkFlowInspection : AbstractBaseUastLocalInspectionTool() {
|
||||
return super.visitReturnExpression(node)
|
||||
}
|
||||
|
||||
override fun visitDeclarationsExpression(node: UDeclarationsExpression): Boolean {
|
||||
node.declarations.forEach {
|
||||
when (it) {
|
||||
is UParameter -> processExpression(it.uastInitializer)
|
||||
is ULocalVariable -> processExpression(it.uastInitializer)
|
||||
is UField -> processExpression(it.uastInitializer)
|
||||
}
|
||||
}
|
||||
return super.visitDeclarationsExpression(node)
|
||||
}
|
||||
|
||||
override fun visitParameter(node: UParameter): Boolean {
|
||||
processExpression(node.uastInitializer)
|
||||
return super.visitParameter(node)
|
||||
}
|
||||
|
||||
override fun visitLocalVariable(node: ULocalVariable): Boolean {
|
||||
processExpression(node.uastInitializer)
|
||||
return super.visitLocalVariable(node)
|
||||
@@ -140,7 +158,14 @@ class SourceToSinkFlowInspection : AbstractBaseUastLocalInspectionTool() {
|
||||
val contextValue: TaintValue = factory.of(annotationContext)
|
||||
if (contextValue !== TaintValue.UNTAINTED) return
|
||||
val taintAnalyzer = TaintAnalyzer(factory)
|
||||
var taintValue = taintAnalyzer.analyzeExpression(expression, false)
|
||||
var taintValue = try {
|
||||
taintAnalyzer.analyzeExpression(expression, false)
|
||||
}
|
||||
catch (e: DeepTaintAnalyzerException) {
|
||||
val errorMessage: String = JvmAnalysisBundle.message("jvm.inspections.source.to.sink.flow.too.complex")
|
||||
holder.registerUProblem(expression, errorMessage, *arrayOf(), highlightType = ProblemHighlightType.WEAK_WARNING)
|
||||
return
|
||||
}
|
||||
taintValue = taintValue.join(contextValue)
|
||||
if (taintValue === TaintValue.UNTAINTED) return
|
||||
val errorMessage = JvmAnalysisBundle.message(taintValue.getErrorMessage(annotationContext))
|
||||
|
||||
@@ -9,13 +9,13 @@ import com.intellij.psi.util.InheritanceUtil
|
||||
import com.intellij.psi.util.PsiUtil
|
||||
import com.intellij.util.SmartList
|
||||
import com.siyeh.ig.psiutils.ClassUtils
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils
|
||||
import one.util.streamex.MoreCollectors
|
||||
import one.util.streamex.StreamEx
|
||||
import org.jetbrains.uast.*
|
||||
import org.jetbrains.uast.UastBinaryOperator.AssignOperator
|
||||
import org.jetbrains.uast.visitor.AbstractUastVisitor
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.stream.Collector
|
||||
|
||||
class TaintAnalyzer(private val myTaintValueFactory: TaintValueFactory) {
|
||||
@@ -24,15 +24,15 @@ class TaintAnalyzer(private val myTaintValueFactory: TaintValueFactory) {
|
||||
private val myVisitedMethods: MutableMap<Pair<PsiElement, List<TaintValue>>, TaintValue> = HashMap()
|
||||
private val myNonMarkedElements: MutableList<NonMarkedElement> = ArrayList()
|
||||
|
||||
//todo add test for new feature
|
||||
private val skipClasses: Set<String> = myTaintValueFactory.getConfiguration()
|
||||
.skipClasses
|
||||
.filterNotNull()
|
||||
.toSet()
|
||||
|
||||
@Throws(DeepTaintAnalyzerException::class)
|
||||
fun analyzeExpression(expression: UExpression, processRecursively: Boolean): TaintValue {
|
||||
val file = expression.getContainingUFile() ?: return TaintValue.UNKNOWN
|
||||
val context = AnalyzeContext(file, processRecursively, false, 0, 20, true)
|
||||
val context = AnalyzeContext.create(file, processRecursively, false, 1, 30, 2, true)
|
||||
return fromExpressionInner(expression, context)
|
||||
}
|
||||
|
||||
@@ -40,26 +40,70 @@ class TaintAnalyzer(private val myTaintValueFactory: TaintValueFactory) {
|
||||
val collectUsages: Boolean,
|
||||
val processOnlyConstant: Boolean,
|
||||
val depthOutside: Int,
|
||||
val depthInside: Int,
|
||||
private val depthInside: AtomicInteger,
|
||||
private val depthNestedMethods: Int,
|
||||
val next: Boolean) {
|
||||
companion object {
|
||||
fun create(file: UFile,
|
||||
collectUsages: Boolean,
|
||||
processOnlyConstant: Boolean,
|
||||
depthOutside: Int,
|
||||
depthInside: Int,
|
||||
depthNestedMethods: Int,
|
||||
next: Boolean): AnalyzeContext {
|
||||
return AnalyzeContext(file, collectUsages, processOnlyConstant, depthOutside, AtomicInteger(depthInside),
|
||||
depthNestedMethods, next)
|
||||
}
|
||||
}
|
||||
|
||||
fun minusMethod(): AnalyzeContext {
|
||||
val depth = depthNestedMethods - 1
|
||||
if (depth < 0) {
|
||||
throw DeepTaintAnalyzerException()
|
||||
}
|
||||
return copy(depthNestedMethods = depth)
|
||||
}
|
||||
|
||||
fun minusInside(): AnalyzeContext {
|
||||
return this.copy(depthInside = depthInside - 1)
|
||||
val depth = depthInside.decrementAndGet()
|
||||
if (depth < 0) {
|
||||
throw DeepTaintAnalyzerException()
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun minusInside(size: Int): AnalyzeContext {
|
||||
val previous = depthInside.get()
|
||||
val next = previous - size
|
||||
depthInside.set(next)
|
||||
if (next < 0) {
|
||||
throw DeepTaintAnalyzerException()
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun notCollect(): AnalyzeContext {
|
||||
if (collectUsages) {
|
||||
return this.copy(collectUsages = false)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun minusOutside(): AnalyzeContext {
|
||||
return this.copy(processOnlyConstant = true, depthOutside = depthOutside - 1)
|
||||
}
|
||||
|
||||
fun notCollect(): AnalyzeContext {
|
||||
return this.copy(collectUsages = false)
|
||||
}
|
||||
|
||||
fun minusInside(size: Int): AnalyzeContext {
|
||||
return this.copy(depthInside = depthInside - size)
|
||||
}
|
||||
|
||||
fun notNext(): AnalyzeContext {
|
||||
return this.copy(next = false)
|
||||
if (next) {
|
||||
return this.copy(next = false)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun checkInside(size: Int) {
|
||||
if (depthInside.get() - size < 0) {
|
||||
throw DeepTaintAnalyzerException()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,22 +111,86 @@ class TaintAnalyzer(private val myTaintValueFactory: TaintValueFactory) {
|
||||
val uResolvable = (expression as? UResolvable) ?: return TaintValue.UNTAINTED
|
||||
if (expression.sourcePsi == null) return TaintValue.UNTAINTED
|
||||
val sourceTarget = uResolvable.resolve()
|
||||
return fromElement(sourceTarget, expression, analyzeContext)
|
||||
if (sourceTarget == null) {
|
||||
return fromCall(expression, analyzeContext) ?: TaintValue.UNKNOWN
|
||||
}
|
||||
val resolvedUElement = uResolvable.resolveToUElement()
|
||||
val sourcePsi = resolvedUElement?.sourcePsi
|
||||
if (sourcePsi != null) {
|
||||
val previousValue = myVisitedTemporary[sourcePsi]
|
||||
if (previousValue != null) {
|
||||
return previousValue
|
||||
}
|
||||
}
|
||||
var taintValue: TaintValue = myTaintValueFactory.fromAnnotation(sourceTarget) ?: return TaintValue.UNTAINTED
|
||||
if (taintValue !== TaintValue.UNKNOWN) return taintValue
|
||||
val value = checkAndPrepareVisited(resolvedUElement)
|
||||
if (value != null) return value
|
||||
taintValue = fromModifierListOwner(sourceTarget, expression, analyzeContext) ?: TaintValue.UNTAINTED
|
||||
addToVisited(resolvedUElement, taintValue)
|
||||
return taintValue
|
||||
}
|
||||
|
||||
private fun fromCall(sourceExpression: UExpression, sourceAnalyzeContext: AnalyzeContext): TaintValue? {
|
||||
var analyzeContext = sourceAnalyzeContext
|
||||
private fun checkAndPrepareVisited(uElement: UElement?, prepare: Boolean = true): TaintValue? {
|
||||
if (uElement == null) return null
|
||||
val sourcePsi = uElement.sourcePsi
|
||||
if (sourcePsi != null) {
|
||||
val value = myVisited[sourcePsi]
|
||||
if (value != null) {
|
||||
return value
|
||||
}
|
||||
if (prepare) {
|
||||
addToVisited(uElement, TaintValue.UNKNOWN)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun addToVisited(uElement: UElement?, result: TaintValue?) {
|
||||
val sourcePsi = uElement?.sourcePsi
|
||||
if (sourcePsi == null) return
|
||||
if (result == null) {
|
||||
myVisited.remove(sourcePsi)
|
||||
return
|
||||
}
|
||||
if (uElement is UVariable) {
|
||||
myVisited[sourcePsi] = result
|
||||
}
|
||||
}
|
||||
|
||||
private fun fromCall(sourceExpression: UExpression, analyzeContext: AnalyzeContext): TaintValue? {
|
||||
var expression = sourceExpression
|
||||
if (analyzeContext.processOnlyConstant) {
|
||||
return null
|
||||
}
|
||||
|
||||
//fields as methods
|
||||
if (expression is UQualifiedReferenceExpression && expression.selector is UReferenceExpression) {
|
||||
val uMethod = expression.resolveToUElement()
|
||||
if (uMethod is UMethod) {
|
||||
val equalFiles = equalFiles(analyzeContext, uMethod)
|
||||
if (myTaintValueFactory.getConfiguration().processMethodAsQualifierAndArguments && !equalFiles) {
|
||||
val fromReceiver = fromExpressionWithoutCollection(expression.receiver, analyzeContext)
|
||||
if (fromReceiver == TaintValue.UNTAINTED && uMethod.uastParameters.isEmpty()) {
|
||||
return fromReceiver
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (expression is UQualifiedReferenceExpression && expression.selector is UCallExpression) {
|
||||
expression = expression.selector
|
||||
}
|
||||
if (expression is UArrayAccessExpression) {
|
||||
analyzeContext.checkInside(1 + expression.indices.size)
|
||||
var result = fromExpressionWithoutCollection(expression.receiver, analyzeContext)
|
||||
expression.indices.forEach { result = result.join(fromExpressionWithoutCollection(it, analyzeContext)) }
|
||||
return result
|
||||
}
|
||||
if (expression !is UCallExpression) {
|
||||
return null
|
||||
}
|
||||
analyzeContext = analyzeContext.minusInside()
|
||||
|
||||
val fromReceiver = fromExpressionWithoutCollection(expression.receiver, analyzeContext)
|
||||
val uMethod = expression.resolveToUElement()
|
||||
if (uMethod is UMethod && equalFiles(analyzeContext, uMethod) && !uMethod.isConstructor) {
|
||||
@@ -91,7 +199,7 @@ class TaintAnalyzer(private val myTaintValueFactory: TaintValueFactory) {
|
||||
(jvmModifiersOwner.hasModifier(JvmModifier.FINAL) || jvmModifiersOwner.hasModifier(JvmModifier.STATIC) ||
|
||||
jvmModifiersOwner.hasModifier(JvmModifier.PRIVATE))) {
|
||||
val values: MutableList<TaintValue> = mutableListOf()
|
||||
analyzeContext = analyzeContext.minusInside(expression.valueArguments.size)
|
||||
analyzeContext.checkInside(expression.valueArguments.size)
|
||||
expression.valueArguments.forEach { argument ->
|
||||
values.add(fromExpressionWithoutCollection(argument, analyzeContext))
|
||||
}
|
||||
@@ -107,49 +215,33 @@ class TaintAnalyzer(private val myTaintValueFactory: TaintValueFactory) {
|
||||
}
|
||||
if (myTaintValueFactory.getConfiguration().processMethodAsQualifierAndArguments) {
|
||||
var taintValue = fromReceiver
|
||||
analyzeContext.checkInside(expression.valueArguments.size)
|
||||
expression.valueArguments.forEach { argument ->
|
||||
taintValue = taintValue.join(fromExpressionWithoutCollection(argument, analyzeContext.minusInside()))
|
||||
taintValue = taintValue.join(fromExpressionWithoutCollection(argument, analyzeContext))
|
||||
}
|
||||
return taintValue
|
||||
}
|
||||
return TaintValue.UNKNOWN
|
||||
}
|
||||
|
||||
private fun fromElement(sourcePsiTarget: PsiElement?, expression: UExpression, analyzeContext: AnalyzeContext): TaintValue {
|
||||
if (sourcePsiTarget == null) {
|
||||
val value = fromCall(expression, analyzeContext)
|
||||
return value ?: TaintValue.UNKNOWN
|
||||
}
|
||||
var value = myVisited[sourcePsiTarget]
|
||||
if (value != null) return value
|
||||
value = myVisitedTemporary[sourcePsiTarget]
|
||||
if (value != null) return value
|
||||
var taintValue: TaintValue = myTaintValueFactory.fromAnnotation(sourcePsiTarget) ?: return TaintValue.UNTAINTED
|
||||
if (taintValue !== TaintValue.UNKNOWN) return taintValue
|
||||
myVisited[sourcePsiTarget] = TaintValue.UNKNOWN
|
||||
taintValue = fromModifierListOwner(sourcePsiTarget, expression, analyzeContext) ?: TaintValue.UNTAINTED
|
||||
myVisited[sourcePsiTarget] = taintValue
|
||||
return taintValue
|
||||
}
|
||||
|
||||
val nonMarkedElements: List<NonMarkedElement>
|
||||
get() = myNonMarkedElements.toList()
|
||||
get() = myNonMarkedElements.distinct().toList()
|
||||
|
||||
private fun fromModifierListOwner(sourcePsiTarget: PsiElement,
|
||||
expression: UExpression,
|
||||
sourceAnalyzeContext: AnalyzeContext): TaintValue? {
|
||||
var analyzeContext = sourceAnalyzeContext
|
||||
analyzeContext: AnalyzeContext): TaintValue? {
|
||||
|
||||
val owner = (sourcePsiTarget as? PsiModifierListOwner) ?: return null
|
||||
analyzeContext = analyzeContext.minusInside()
|
||||
if (analyzeContext.depthInside < 0) return null
|
||||
var taintValue = fromLocalVar(expression, owner, analyzeContext)
|
||||
if (taintValue != null) return taintValue
|
||||
taintValue = fromField(owner, analyzeContext)
|
||||
if (taintValue != null) {
|
||||
return taintValue
|
||||
}
|
||||
taintValue = fromField(expression, owner, analyzeContext)
|
||||
if (taintValue == null) {
|
||||
taintValue = fromCall(expression, analyzeContext)
|
||||
}
|
||||
if (taintValue == null) {
|
||||
taintValue = fromParam(owner, analyzeContext)
|
||||
taintValue = fromParam(expression, owner, analyzeContext)
|
||||
}
|
||||
if (taintValue == null) {
|
||||
//it might be kotlin param, for example
|
||||
@@ -174,8 +266,8 @@ class TaintAnalyzer(private val myTaintValueFactory: TaintValueFactory) {
|
||||
if (sourceTarget !is PsiLocalVariable) return null
|
||||
val localVariable = (expression as? UResolvable)?.resolveToUElement() as? ULocalVariable ?: return null
|
||||
val containingMethod = localVariable.getContainingUMethod() ?: return null
|
||||
val checkLocalAfterUsing = possibleToSkipCheckAfterReference(expression, containingMethod)
|
||||
return fromVar(sourceTarget, analyzeContext, if (checkLocalAfterUsing) expression else null)
|
||||
val skipAfterReference = possibleToSkipCheckAfterReference(expression, containingMethod)
|
||||
return fromVar(sourceTarget, analyzeContext, expression, skipAfterReference)
|
||||
}
|
||||
|
||||
//Kotlin allows use non-effective-final variables in lambdas
|
||||
@@ -192,9 +284,11 @@ class TaintAnalyzer(private val myTaintValueFactory: TaintValueFactory) {
|
||||
return checkLocalAfterUsing
|
||||
}
|
||||
|
||||
private fun fromVar(sourceTarget: PsiElement, analyzeContext: AnalyzeContext, usedReference: UExpression? = null): TaintValue? {
|
||||
val psiVariable = (sourceTarget as? PsiVariable) ?: return null
|
||||
val uVariable = psiVariable.toUElement(UVariable::class.java) ?: return null
|
||||
private fun fromVar(sourceTarget: PsiElement,
|
||||
analyzeContext: AnalyzeContext,
|
||||
usedReference: UExpression?,
|
||||
skipAfterReference: Boolean): TaintValue? {
|
||||
val uVariable = sourceTarget.toUElement(UVariable::class.java) ?: return null
|
||||
val uInitializer = uVariable.uastInitializer
|
||||
val taintValue = fromExpressionWithoutCollection(uInitializer, analyzeContext)
|
||||
if (taintValue == TaintValue.TAINTED) return taintValue
|
||||
@@ -205,14 +299,15 @@ class TaintAnalyzer(private val myTaintValueFactory: TaintValueFactory) {
|
||||
else if (uVariable is UField) {
|
||||
codeBlock = uVariable.uastParent
|
||||
}
|
||||
return codeBlock?.let { analyzeVar(taintValue, it, psiVariable, analyzeContext, usedReference) } ?: taintValue
|
||||
return codeBlock?.let { analyzeVar(taintValue, it, uVariable, analyzeContext, usedReference, skipAfterReference) } ?: taintValue
|
||||
}
|
||||
|
||||
private fun analyzeVar(taintValue: TaintValue,
|
||||
codeBlock: UElement,
|
||||
psiVariable: PsiVariable,
|
||||
uVariable: UVariable,
|
||||
analyzeContext: AnalyzeContext,
|
||||
usedReference: UExpression? = null): TaintValue {
|
||||
usedReference: UExpression?,
|
||||
skipAfterReference: Boolean): TaintValue {
|
||||
|
||||
class VarAnalyzer(var myTaintValue: TaintValue) : AbstractUastVisitor() {
|
||||
|
||||
@@ -221,13 +316,15 @@ class TaintAnalyzer(private val myTaintValueFactory: TaintValueFactory) {
|
||||
for (expression in node.expressions) {
|
||||
expression.accept(this)
|
||||
if (stopAnalysis) return true
|
||||
if (myTaintValue == TaintValue.TAINTED) return true
|
||||
if (myTaintValue == TaintValue.TAINTED) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun visitExpression(node: UExpression): Boolean {
|
||||
if (usedReference == node) {
|
||||
if (skipAfterReference && usedReference == node) {
|
||||
stopAnalysis = true
|
||||
return true
|
||||
}
|
||||
@@ -244,10 +341,10 @@ class TaintAnalyzer(private val myTaintValueFactory: TaintValueFactory) {
|
||||
}
|
||||
|
||||
private fun checkUsages(expressions: List<UExpression?>): TaintValue {
|
||||
if (skipClass(psiVariable.type)) {
|
||||
if (skipClass(uVariable.type)) {
|
||||
return TaintValue.UNTAINTED
|
||||
}
|
||||
if (ClassUtils.isImmutable(psiVariable.type)) {
|
||||
if (ClassUtils.isImmutable(uVariable.type)) {
|
||||
return TaintValue.UNTAINTED
|
||||
}
|
||||
for (expression in expressions) {
|
||||
@@ -258,13 +355,21 @@ class TaintAnalyzer(private val myTaintValueFactory: TaintValueFactory) {
|
||||
expression.accept(object : AbstractUastVisitor() {
|
||||
override fun visitExpression(node: UExpression): Boolean {
|
||||
if (node is UReferenceExpression) {
|
||||
if (node.resolve() == psiVariable) {
|
||||
if ((usedReference == null || node.sourcePsi != usedReference.sourcePsi) &&
|
||||
uVariable.sourcePsi != null && node.resolveToUElement()?.sourcePsi == uVariable.sourcePsi) {
|
||||
hasUsage.set(true)
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
return super.visitExpression(node)
|
||||
}
|
||||
|
||||
override fun visitElement(node: UElement): Boolean {
|
||||
if (hasUsage.get()) {
|
||||
return true
|
||||
}
|
||||
return super.visitElement(node)
|
||||
}
|
||||
})
|
||||
if (hasUsage.get()) {
|
||||
return TaintValue.UNKNOWN
|
||||
@@ -275,8 +380,9 @@ class TaintAnalyzer(private val myTaintValueFactory: TaintValueFactory) {
|
||||
|
||||
override fun visitBinaryExpression(node: UBinaryExpression): Boolean {
|
||||
if (node.operator !is AssignOperator) return super.visitBinaryExpression(node)
|
||||
val lhs = (node.leftOperand as? UReferenceExpression)
|
||||
if (lhs == null || psiVariable != lhs.resolve()) return super.visitBinaryExpression(node)
|
||||
val lhs = (node.leftOperand as? UReferenceExpression) ?: return super.visitBinaryExpression(node)
|
||||
val uElement = lhs.resolveToUElement()
|
||||
if (uElement == null || uVariable.sourcePsi != uElement.sourcePsi) return super.visitBinaryExpression(node)
|
||||
val rhs = node.rightOperand
|
||||
myTaintValue = myTaintValue.join(fromExpressionWithoutCollection(rhs, analyzeContext))
|
||||
return if (myTaintValue == TaintValue.TAINTED) true else super.visitBinaryExpression(node)
|
||||
@@ -288,16 +394,17 @@ class TaintAnalyzer(private val myTaintValueFactory: TaintValueFactory) {
|
||||
return varAnalyzer.myTaintValue
|
||||
}
|
||||
|
||||
private fun fromParam(target: PsiElement?, analyzeContext: AnalyzeContext): TaintValue? {
|
||||
private fun fromParam(expression: UExpression, target: PsiElement?, analyzeContext: AnalyzeContext): TaintValue? {
|
||||
val psiParameter = (target as? PsiParameter) ?: return null
|
||||
val uParameter = target.toUElement(UParameter::class.java) ?: return null
|
||||
// default parameter value
|
||||
val uInitializer = uParameter.uastInitializer
|
||||
var taintValue = fromExpressionWithoutCollection(uInitializer, analyzeContext.minusInside())
|
||||
var taintValue = fromExpressionWithoutCollection(uInitializer, analyzeContext)
|
||||
if (taintValue == TaintValue.TAINTED) return taintValue
|
||||
val uMethod = (uParameter.uastParent as? UMethod) ?: return TaintValue.UNTAINTED
|
||||
val uBlock = (uMethod.uastBody as? UBlockExpression)
|
||||
if (uBlock != null) taintValue = analyzeVar(taintValue, uBlock, psiParameter, analyzeContext)
|
||||
if (uBlock != null) taintValue = analyzeVar(taintValue, uBlock, uParameter, analyzeContext, expression,
|
||||
possibleToSkipCheckAfterReference(expression, uMethod))
|
||||
if (taintValue == TaintValue.TAINTED) return taintValue
|
||||
val nonMarkedElements = SmartList<NonMarkedElement?>()
|
||||
// this might happen when we analyze kotlin primary constructor parameter
|
||||
@@ -311,30 +418,27 @@ class TaintAnalyzer(private val myTaintValueFactory: TaintValueFactory) {
|
||||
return TaintValue.UNKNOWN
|
||||
}
|
||||
|
||||
private fun fromField(target: PsiElement, analyzeContext: AnalyzeContext): TaintValue? {
|
||||
private fun fromField(expression: UExpression?, target: PsiElement, analyzeContext: AnalyzeContext): TaintValue? {
|
||||
if (analyzeContext.depthOutside < 0) return null
|
||||
val uElement = target.toUElement() as? UField ?: return null
|
||||
val sourcePsi = uElement.sourcePsi ?: return null
|
||||
val jvmModifiersOwner: JvmModifiersOwner = uElement
|
||||
if (jvmModifiersOwner.hasModifier(JvmModifier.FINAL) || (jvmModifiersOwner.hasModifier(
|
||||
JvmModifier.PRIVATE) && canFieldAssignOnlyInConstructors(
|
||||
target))) {
|
||||
val result: TaintValue? = if (!equalFiles(analyzeContext, uElement)) {
|
||||
fromVar(sourcePsi, analyzeContext.minusOutside().notCollect())
|
||||
}
|
||||
else {
|
||||
fromVar(sourcePsi, analyzeContext.notCollect())
|
||||
}
|
||||
|
||||
if (result != null) {
|
||||
return result
|
||||
}
|
||||
return if (jvmModifiersOwner.hasModifier(JvmModifier.FINAL)) {
|
||||
TaintValue.UNTAINTED
|
||||
}
|
||||
else {
|
||||
TaintValue.UNKNOWN
|
||||
}
|
||||
val equalFiles = equalFiles(analyzeContext, uElement)
|
||||
if (!equalFiles &&
|
||||
jvmModifiersOwner.hasModifier(JvmModifier.FINAL) &&
|
||||
expression is UQualifiedReferenceExpression &&
|
||||
skipClass(expression.receiver.getExpressionType())) {
|
||||
return TaintValue.UNTAINTED
|
||||
}
|
||||
if (equalFiles &&
|
||||
(jvmModifiersOwner.hasModifier(JvmModifier.FINAL) ||
|
||||
(jvmModifiersOwner.hasModifier(JvmModifier.PRIVATE) && fieldAssignedOnlyWithLiterals(target, analyzeContext)))) {
|
||||
val result: TaintValue? = fromVar(sourcePsi, analyzeContext.notCollect().minusInside(), expression, false)
|
||||
return result ?: TaintValue.UNKNOWN
|
||||
}
|
||||
if (!equalFiles && jvmModifiersOwner.hasModifier(JvmModifier.FINAL) && uElement.uastInitializer != null) {
|
||||
return fromExpressionWithoutCollection(uElement.uastInitializer,
|
||||
analyzeContext.notNext().minusOutside())
|
||||
}
|
||||
if (analyzeContext.processOnlyConstant) {
|
||||
return null
|
||||
@@ -362,38 +466,43 @@ class TaintAnalyzer(private val myTaintValueFactory: TaintValueFactory) {
|
||||
return analyzeMethod(uMethod, analyzeContext, values)
|
||||
}
|
||||
|
||||
private fun analyzeMethod(uMethod: UMethod, analyzeContext: AnalyzeContext, arguments: List<TaintValue>): TaintValue {
|
||||
if (!equalFiles(analyzeContext, uMethod)) return TaintValue.UNKNOWN
|
||||
private fun analyzeMethod(uMethod: UMethod, sourceContext: AnalyzeContext, arguments: List<TaintValue>): TaintValue {
|
||||
if (!equalFiles(sourceContext, uMethod)) return TaintValue.UNKNOWN
|
||||
val psiElement = uMethod.sourcePsi ?: return TaintValue.UNKNOWN
|
||||
val key = Pair(psiElement, arguments)
|
||||
val value = myVisitedMethods[key]
|
||||
if (value != null) return value
|
||||
class MethodAnalyzer : AbstractUastVisitor() {
|
||||
var myTaintValue = TaintValue.UNTAINTED
|
||||
override fun visitBlockExpression(node: UBlockExpression): Boolean {
|
||||
for (expression in node.expressions) {
|
||||
expression.accept(this)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun visitReturnExpression(node: UReturnExpression): Boolean {
|
||||
val returnExpression = node.returnExpression ?: return true
|
||||
myTaintValue = myTaintValue.join(fromExpressionWithoutCollection(returnExpression, analyzeContext.minusInside()))
|
||||
return if (myTaintValue == TaintValue.TAINTED) true else super.visitReturnExpression(node)
|
||||
}
|
||||
if (value != null) {
|
||||
return value
|
||||
}
|
||||
|
||||
var analyzeContext = sourceContext
|
||||
|
||||
val methodBody = (uMethod.uastBody as? UBlockExpression)
|
||||
if (methodBody == null) {
|
||||
// maybe it is a generated kotlin property getter or setter
|
||||
val sourcePsi = uMethod.sourcePsi ?: return TaintValue.UNTAINTED
|
||||
val taintValue = fromField(sourcePsi, analyzeContext)
|
||||
val taintValue = fromField(null, sourcePsi, analyzeContext.minusInside())
|
||||
return taintValue ?: TaintValue.UNTAINTED
|
||||
}
|
||||
|
||||
analyzeContext = analyzeContext.minusInside().minusMethod()
|
||||
|
||||
|
||||
class MethodAnalyzer : AbstractUastVisitor() {
|
||||
var myTaintValue = TaintValue.UNTAINTED
|
||||
override fun visitReturnExpression(node: UReturnExpression): Boolean {
|
||||
val returnExpression = node.returnExpression ?: return true
|
||||
myTaintValue = myTaintValue.join(fromExpressionWithoutCollection(returnExpression, analyzeContext))
|
||||
return if (myTaintValue == TaintValue.TAINTED) true else super.visitReturnExpression(node)
|
||||
}
|
||||
}
|
||||
|
||||
val methodAnalyzer = MethodAnalyzer()
|
||||
myVisitedMethods[key] = TaintValue.UNKNOWN
|
||||
val previous = HashMap(myVisitedTemporary)
|
||||
|
||||
val previousVisited = HashMap(myVisited)
|
||||
val previousTemporary = HashMap(myVisitedTemporary)
|
||||
|
||||
if (!arguments.isEmpty()) {
|
||||
val parameters = uMethod.uastParameters
|
||||
for (i in parameters.indices) {
|
||||
@@ -401,60 +510,93 @@ class TaintAnalyzer(private val myTaintValueFactory: TaintValueFactory) {
|
||||
myVisitedTemporary[sourcePsi] = if (arguments.size <= i) arguments[arguments.size - 1] else arguments[i]
|
||||
}
|
||||
}
|
||||
|
||||
//prevent recursion always
|
||||
myVisited[psiElement] = TaintValue.UNKNOWN
|
||||
|
||||
methodBody.accept(methodAnalyzer)
|
||||
val returnValue = methodAnalyzer.myTaintValue
|
||||
|
||||
myVisitedTemporary.clear()
|
||||
myVisitedTemporary.putAll(previous)
|
||||
myVisitedTemporary.putAll(previousTemporary)
|
||||
|
||||
myVisited.clear()
|
||||
myVisited.putAll(previousVisited)
|
||||
|
||||
myVisitedMethods[key] = returnValue
|
||||
return returnValue
|
||||
}
|
||||
|
||||
private fun fromExpressionWithoutCollection(uExpression: UExpression?, sourceAnalyzeContext: AnalyzeContext): TaintValue {
|
||||
var analyzeContext = sourceAnalyzeContext
|
||||
analyzeContext = analyzeContext.notCollect()
|
||||
private fun fromExpressionWithoutCollection(uExpression: UExpression?, analyzeContext: AnalyzeContext): TaintValue {
|
||||
return fromExpressionInner(uExpression, analyzeContext.notCollect())
|
||||
}
|
||||
|
||||
private fun fromExpressionInner(sourceUExpression: UExpression?, analyzeContext: AnalyzeContext): TaintValue {
|
||||
var uExpression = sourceUExpression ?: return TaintValue.UNTAINTED
|
||||
if (analyzeContext.depthInside < 0) return TaintValue.UNKNOWN
|
||||
if (analyzeContext.depthOutside < 0) return TaintValue.UNKNOWN
|
||||
val type = uExpression.getExpressionType()
|
||||
if (type != null && skipClass(type)) return TaintValue.UNTAINTED
|
||||
if (uExpression is UThisExpression) return TaintValue.UNTAINTED
|
||||
uExpression = uExpression.skipParenthesizedExprDown()
|
||||
if (uExpression is ULiteralExpression) return TaintValue.UNTAINTED
|
||||
if (uExpression is UResolvable) return analyzeInner(uExpression, analyzeContext)
|
||||
val uConcatenation = getConcatenation(uExpression)
|
||||
if (uConcatenation != null) {
|
||||
val size = uConcatenation.operands.size
|
||||
return StreamEx.of(uConcatenation.operands).collect(joining(analyzeContext.minusInside(size)))
|
||||
val operands = uConcatenation.operands
|
||||
val size = operands.filter { it !is ULiteralExpression && it !is UPolyadicExpression }.size
|
||||
return StreamEx.of(operands).collect(joining(analyzeContext.minusInside(size)))
|
||||
}
|
||||
val uIfExpression = (uExpression as? UIfExpression)
|
||||
if (uIfExpression != null) {
|
||||
return StreamEx.of(uIfExpression.thenExpression, uIfExpression.elseExpression)
|
||||
.collect(joining(analyzeContext.minusInside(2)))
|
||||
when (uExpression) {
|
||||
is UUnknownExpression -> {
|
||||
return TaintValue.UNKNOWN
|
||||
}
|
||||
is UThisExpression -> {
|
||||
return TaintValue.UNTAINTED
|
||||
}
|
||||
is ULiteralExpression -> {
|
||||
return TaintValue.UNTAINTED
|
||||
}
|
||||
is ULambdaExpression -> {
|
||||
return TaintValue.UNKNOWN
|
||||
}
|
||||
is UClassLiteralExpression -> {
|
||||
return TaintValue.UNTAINTED
|
||||
}
|
||||
is UResolvable -> {
|
||||
val resolved = uExpression.resolveToUElement()
|
||||
val visited = checkAndPrepareVisited(resolved, prepare = false)
|
||||
if (visited != null) {
|
||||
return visited
|
||||
}
|
||||
return analyzeInner(uExpression, analyzeContext.minusInside())
|
||||
}
|
||||
is UUnaryExpression -> {
|
||||
return fromExpressionWithoutCollection(uExpression.operand, analyzeContext.minusInside())
|
||||
}
|
||||
is UBinaryExpression -> {
|
||||
return StreamEx.of(uExpression.leftOperand, uExpression.rightOperand)
|
||||
.collect(joining(analyzeContext.minusInside(2)))
|
||||
}
|
||||
is ULabeledExpression -> {
|
||||
return fromExpressionWithoutCollection(uExpression.expression, analyzeContext.minusInside())
|
||||
}
|
||||
is UIfExpression, is USwitchExpression, is UBlockExpression -> {
|
||||
val nonStructuralChildren = nonStructuralChildren(uExpression).toList()
|
||||
return StreamEx.of(nonStructuralChildren)
|
||||
.filter { it != null }
|
||||
.collect(joining(analyzeContext.minusInside(nonStructuralChildren.size)))
|
||||
}
|
||||
else -> {
|
||||
return TaintValue.UNKNOWN
|
||||
}
|
||||
}
|
||||
val switchExpression = (uExpression as? USwitchExpression)
|
||||
if (switchExpression != null) {
|
||||
val size = switchExpression.body.expressions.size
|
||||
return StreamEx.of(switchExpression.body.expressions)
|
||||
.collect(joining(analyzeContext.minusInside(size)))
|
||||
}
|
||||
if (uExpression is ULambdaExpression) return TaintValue.UNKNOWN
|
||||
val javaPsi = (uExpression.javaPsi as? PsiExpression) ?: return TaintValue.UNTAINTED
|
||||
val list = ExpressionUtils.nonStructuralChildren(javaPsi).toList()
|
||||
return StreamEx.of(list)
|
||||
.map { e: PsiExpression -> e.toUElement(UExpression::class.java) }
|
||||
.collect(joining(analyzeContext.minusInside(list.size)))
|
||||
}
|
||||
|
||||
private fun skipClass(type: PsiType): Boolean {
|
||||
private fun skipClass(type: PsiType?): Boolean {
|
||||
if (type == null) return false
|
||||
val aClass = PsiUtil.resolveClassInClassTypeOnly(type)
|
||||
return skipClasses.contains(type.canonicalText) ||
|
||||
skipClasses.any { cl: String? ->
|
||||
cl != null && InheritanceUtil.isInheritor(type, cl)
|
||||
} ||
|
||||
(aClass != null && (aClass.isInterface || aClass.isEnum))
|
||||
(aClass != null && (aClass.isAnnotationType || aClass.isEnum))
|
||||
}
|
||||
|
||||
private fun joining(analyzeContext: AnalyzeContext): Collector<UExpression?, *, TaintValue> {
|
||||
@@ -468,30 +610,42 @@ class TaintAnalyzer(private val myTaintValueFactory: TaintValueFactory) {
|
||||
return file != null && analyzeContext.file.sourcePsi == file.sourcePsi
|
||||
}
|
||||
|
||||
private fun canFieldAssignOnlyInConstructors(target: PsiElement): Boolean {
|
||||
private fun fieldAssignedOnlyWithLiterals(target: PsiElement, analyzeContext: AnalyzeContext): Boolean {
|
||||
val uElement = target.toUElement()
|
||||
if (uElement == null) return false
|
||||
val containingUClass = uElement.getContainingUClass() ?: return false
|
||||
|
||||
class FieldAssignmentAnalyzer : AbstractUastVisitor() {
|
||||
var assignOutsideConstructor = false
|
||||
var assignOnlyWithLiteral = true
|
||||
override fun visitBinaryExpression(node: UBinaryExpression): Boolean {
|
||||
if (node.operator !is AssignOperator) return super.visitBinaryExpression(node)
|
||||
val lhs = (node.leftOperand as? UReferenceExpression)
|
||||
if (lhs == null || target != lhs.resolve()) return super.visitBinaryExpression(node)
|
||||
val containingUMethod = node.getContainingUMethod() ?: return super.visitBinaryExpression(node)
|
||||
if (!containingUMethod.isConstructor) {
|
||||
assignOutsideConstructor = true
|
||||
if (node.rightOperand !is ULiteralExpression) {
|
||||
//don't go further, consider that it is already untidy
|
||||
assignOnlyWithLiteral = false
|
||||
return true
|
||||
}
|
||||
return super.visitBinaryExpression(node)
|
||||
}
|
||||
}
|
||||
|
||||
return containingUClass.methods.none {
|
||||
val methods = listOf(containingUClass, *containingUClass.innerClasses)
|
||||
.map { it.methods.toList() }
|
||||
.flatten()
|
||||
analyzeContext.minusInside(methods.size)
|
||||
return methods.all {
|
||||
val visitor = FieldAssignmentAnalyzer()
|
||||
it.accept(visitor)
|
||||
!it.isConstructor && visitor.assignOutsideConstructor
|
||||
if (it.javaPsi.isPhysical) {
|
||||
it.accept(visitor)
|
||||
return@all visitor.assignOnlyWithLiteral
|
||||
}
|
||||
val body = it.javaPsi.body.toUElement()
|
||||
if (body != null) {
|
||||
body.accept(visitor)
|
||||
return@all visitor.assignOnlyWithLiteral
|
||||
}
|
||||
return@all !(it.sourcePsi == target && it.javaPsi.parameters.isNotEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,3 +695,5 @@ class TaintAnalyzer(private val myTaintValueFactory: TaintValueFactory) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DeepTaintAnalyzerException : RuntimeException()
|
||||
@@ -98,7 +98,13 @@ public class TaintNode extends PresentableNodeDescriptor<TaintNode> {
|
||||
TaintAnalyzer taintAnalyzer = new TaintAnalyzer(myTaintValueFactory);
|
||||
UExpression uExpression = UastContextKt.toUElementOfExpectedTypes(elementRef, UCallExpression.class, UReferenceExpression.class);
|
||||
if(uExpression == null) return Collections.emptyList();
|
||||
TaintValue taintValue = taintAnalyzer.analyzeExpression(uExpression, true);
|
||||
TaintValue taintValue;
|
||||
try {
|
||||
taintValue = taintAnalyzer.analyzeExpression(uExpression, true);
|
||||
}
|
||||
catch (DeepTaintAnalyzerException e) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
myTaintValue = taintValue;
|
||||
if (taintValue == TaintValue.UNTAINTED) return Collections.emptyList();
|
||||
if (taintValue == TaintValue.TAINTED) {
|
||||
|
||||
@@ -5,13 +5,15 @@ import com.intellij.codeInsight.AnnotationTargetUtil
|
||||
import com.intellij.codeInsight.AnnotationUtil
|
||||
import com.intellij.codeInsight.ExternalAnnotationsManager
|
||||
import com.intellij.codeInspection.restriction.AnnotationContext
|
||||
import com.intellij.codeInspection.restriction.RestrictionInfo
|
||||
import com.intellij.codeInspection.restriction.RestrictionInfo.RestrictionInfoKind
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.util.PsiUtil
|
||||
import com.siyeh.ig.psiutils.MethodMatcher
|
||||
import org.jetbrains.uast.UAnnotation
|
||||
import org.jetbrains.uast.ULocalVariable
|
||||
import org.jetbrains.uast.UVariable
|
||||
import org.jetbrains.uast.toUElement
|
||||
import kotlin.streams.asSequence
|
||||
|
||||
@@ -72,19 +74,25 @@ class TaintValueFactory(private val myConfiguration: UntaintedConfiguration) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (info.kind != RestrictionInfo.RestrictionInfoKind.KNOWN) {
|
||||
if (info.kind != RestrictionInfoKind.KNOWN) {
|
||||
info = context.secondaryItems().asSequence()
|
||||
.map { fromAnnotationOwner(it.modifierList) }
|
||||
.filter { it !== TaintValue.UNKNOWN }
|
||||
.flatMap { listOf(fromAnnotation(it), fromAnnotationOwner(it.modifierList)) }
|
||||
.filter { it != null && it !== TaintValue.UNKNOWN }
|
||||
.firstOrNull() ?: info
|
||||
}
|
||||
if (info === TaintValue.UNKNOWN) {
|
||||
if (info == TaintValue.UNKNOWN) {
|
||||
val obj: Any = if (owner is PsiParameter) owner.declarationScope else owner
|
||||
val member = (obj as? PsiMember)
|
||||
if (member != null) {
|
||||
info = of(member)
|
||||
}
|
||||
}
|
||||
val toUElement = owner.toUElement()
|
||||
if (info == TaintValue.UNKNOWN && toUElement is UVariable) {
|
||||
return toUElement.uAnnotations
|
||||
.mapNotNull { fromUAnnotation(it) }
|
||||
.firstOrNull { it != TaintValue.UNKNOWN } ?: info
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
@@ -126,7 +134,20 @@ class TaintValueFactory(private val myConfiguration: UntaintedConfiguration) {
|
||||
if (myTaintedAnnotations.contains(annotationQualifiedName)) {
|
||||
return TaintValue.TAINTED
|
||||
}
|
||||
return if (myUnTaintedAnnotations.contains(annotationQualifiedName)) {
|
||||
return if (myUnTaintedAnnotations.contains(annotationQualifiedName) && annotationQualifiedName != JAVAX_ANNOTATION_UNTAINTED) {
|
||||
TaintValue.UNTAINTED
|
||||
}
|
||||
else null
|
||||
}
|
||||
|
||||
private fun fromUAnnotation(annotation: UAnnotation): TaintValue? {
|
||||
val annotationQualifiedName = annotation.qualifiedName
|
||||
val fromJsr = processUJsr(annotationQualifiedName, annotation)
|
||||
if (fromJsr != null) return fromJsr
|
||||
if (myTaintedAnnotations.contains(annotationQualifiedName)) {
|
||||
return TaintValue.TAINTED
|
||||
}
|
||||
return if (myUnTaintedAnnotations.contains(annotationQualifiedName) && annotationQualifiedName != JAVAX_ANNOTATION_UNTAINTED) {
|
||||
TaintValue.UNTAINTED
|
||||
}
|
||||
else null
|
||||
@@ -138,8 +159,20 @@ class TaintValueFactory(private val myConfiguration: UntaintedConfiguration) {
|
||||
!myUnTaintedAnnotations.contains(JAVAX_ANNOTATION_UNTAINTED)) {
|
||||
return null
|
||||
}
|
||||
val whenAttribute = annotation.findAttributeValue("when") ?: return null
|
||||
return if (whenAttribute.textMatches("ALWAYS")) TaintValue.UNTAINTED else null
|
||||
val whenAttribute = annotation.findAttributeValue("when") ?: return TaintValue.UNTAINTED
|
||||
return if (whenAttribute.textMatches("ALWAYS") || whenAttribute.textMatches(
|
||||
"javax.annotation.meta.When.ALWAYS")) TaintValue.UNTAINTED
|
||||
else null
|
||||
}
|
||||
|
||||
private fun processUJsr(qualifiedName: String?, annotation: UAnnotation): TaintValue? {
|
||||
if (qualifiedName == null ||
|
||||
qualifiedName != JAVAX_ANNOTATION_UNTAINTED ||
|
||||
!myUnTaintedAnnotations.contains(JAVAX_ANNOTATION_UNTAINTED)) {
|
||||
return null
|
||||
}
|
||||
val whenAttribute = annotation.findAttributeValue("when") ?: return TaintValue.UNTAINTED
|
||||
return if ((whenAttribute.evaluate() as? Pair<*, *>)?.second.toString() == "ALWAYS") TaintValue.UNTAINTED else null
|
||||
}
|
||||
|
||||
fun fromAnnotation(target: PsiElement?): TaintValue? {
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import org.checkerframework.checker.tainting.qual.Untainted;
|
||||
|
||||
class CallsCheck {
|
||||
|
||||
public void testCall(String dirty, @Untainted String clean) {
|
||||
sink(<warning descr="Unknown string is used as safe parameter">dirty</warning>); //warn
|
||||
sink("");
|
||||
sink(cleanMethod());
|
||||
sink(<warning descr="Unknown string is used as safe parameter">publicMethod()</warning>); //warn
|
||||
sink(publicFinalMethod());
|
||||
sink(<warning descr="Unknown string is used as safe parameter">privateDirty(dirty)</warning>); //warn
|
||||
sink(<warning descr="Unknown string is used as safe parameter">dirty.toLowerCase()</warning>); //warn
|
||||
sink(dirty.getClass().getName());
|
||||
sink(<warning descr="Unknown string is used as safe parameter">dirty.replace("1", "2")</warning>); //warn
|
||||
sink(clean);
|
||||
sink(<warning descr="Unknown string is used as safe parameter">clean.replace("1", dirty)</warning>); //warn
|
||||
}
|
||||
|
||||
private String privateDirty(String dirty) {
|
||||
return dirty;
|
||||
}
|
||||
|
||||
public String publicMethod() {
|
||||
return "1";
|
||||
}
|
||||
public final String publicFinalMethod() {
|
||||
return "1";
|
||||
}
|
||||
|
||||
private String cleanMethod() {
|
||||
return "null";
|
||||
}
|
||||
|
||||
public void sink(@Untainted String clean) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import org.checkerframework.checker.tainting.qual.Untainted;
|
||||
|
||||
public class DifferentExpression {
|
||||
|
||||
public void test() {
|
||||
sink(this.toString());
|
||||
Runnable r = () -> {
|
||||
};
|
||||
sink(<warning descr="Unknown string is used as safe parameter">r.toString()</warning>); //warn
|
||||
sink(DifferentExpression.class.toString());
|
||||
sink("test" + (1 - 1));
|
||||
int x = 1;
|
||||
sink("test" + (++x));
|
||||
sink(<warning descr="Unknown string is used as safe parameter">param2("1",<error descr="Expression expected"> </error>)</warning>); //warn
|
||||
}
|
||||
|
||||
|
||||
public static void sink(@Untainted String string) {
|
||||
|
||||
}
|
||||
|
||||
public static String param2(String t, String t1) {
|
||||
return t1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import org.checkerframework.checker.tainting.qual.Untainted;
|
||||
|
||||
class LocalCheck {
|
||||
|
||||
|
||||
public enum State {
|
||||
OFF, ON
|
||||
}
|
||||
|
||||
public @interface InterfaceSomething {
|
||||
|
||||
}
|
||||
|
||||
void test(@Untainted String clean, String dirty, State state, InterfaceSomething interfaceSomething) {
|
||||
sink(clean);
|
||||
sink(<warning descr="Unknown string is used as safe parameter">dirty</warning>); //warn
|
||||
sink(state.name());
|
||||
sink(interfaceSomething.toString());
|
||||
}
|
||||
|
||||
void sink(@Untainted String clean) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import org.checkerframework.checker.tainting.qual.Untainted;
|
||||
|
||||
class FieldsCheck {
|
||||
final String constant = "1";
|
||||
private String clean = "1";
|
||||
private String notClean = "1";
|
||||
|
||||
final String finalAppliedFromConstructor;
|
||||
private String appliedFromConstructor;
|
||||
private String clean2;
|
||||
|
||||
public FieldsCheck(String finalAppliedFromConstructor, String appliedFromConstructor) {
|
||||
this.finalAppliedFromConstructor = finalAppliedFromConstructor;
|
||||
this.appliedFromConstructor = appliedFromConstructor;
|
||||
clean2 = "2";
|
||||
}
|
||||
|
||||
public void setNotClean(String notClean) {
|
||||
this.notClean = notClean;
|
||||
}
|
||||
|
||||
public void test() {
|
||||
sink(constant);
|
||||
sink(clean);
|
||||
sink(clean2);
|
||||
sink(<warning descr="Unknown string is used as safe parameter">notClean</warning>); //warn
|
||||
sink(<warning descr="Unknown string is used as safe parameter">finalAppliedFromConstructor</warning>); //warn
|
||||
sink(<warning descr="Unknown string is used as safe parameter">appliedFromConstructor</warning>); //warn
|
||||
}
|
||||
|
||||
void sink(@Untainted String s) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
import org.checkerframework.checker.tainting.qual.Untainted;
|
||||
|
||||
class Limit {
|
||||
|
||||
public final static String fromAnotherFile = Limit2.fromAnotherFile;
|
||||
public final static String fromAnotherFile2 = Limit2.fromAnotherFile2;
|
||||
public final static String fromAnotherFile3 = Limit2.fromAnotherFile3;
|
||||
public final static String fromAnotherFile4 = Limit2.fromAnotherFile4;
|
||||
public final static String fromAnotherFile5 = new Limit2().fromAnotherFile5;
|
||||
public final static String fromAnotherFile6 = new Limit2().fromAnotherFile6;
|
||||
|
||||
public static void test(@Untainted String clear, String dirty) {
|
||||
sink(<warning descr="Unknown string is used as safe parameter">dirty</warning>); //warn
|
||||
sink(<warning descr="Unknown string is used as safe parameter">next(next(next(next(next(next(next(next(next(next(next(next(next(next(next(dirty)))))))))))))))</warning>); //warn complex
|
||||
sink(<warning descr="Unknown string is used as safe parameter">next(next(next(next(next(next(next(dirty)))))))</warning>); //warn
|
||||
sink(next(next(next(next(next(next(next(clear))))))));
|
||||
sink(<weak_warning descr="Too complex to check that the string is safe in a safe context">next(next(next(next(next(next(next(clear))))))) +
|
||||
next(next(next(next(next(next(next(clear))))))) +
|
||||
next(next(next(next(next(next(next(clear))))))) +
|
||||
next(next(next(next(next(next(next(clear))))))) +
|
||||
next(next(next(next(next(next(next(clear))))))) +
|
||||
next(next(next(next(next(next(next(clear)))))))</weak_warning>);
|
||||
sink(<weak_warning descr="Too complex to check that the string is safe in a safe context">next(next(next(next(next(next(next(clear))))))) +
|
||||
next(next(next(next(next(next(next(clear))))))) +
|
||||
next(next(next(next(next(next(next(clear))))))) +
|
||||
next(next(next(next(next(next(next(clear))))))) +
|
||||
next(next(next(next(next(next(next(clear))))))) +
|
||||
next(next(next(next(next(next(next(dirty)))))))</weak_warning>); //warn
|
||||
sink(fromAnotherFile);
|
||||
sink(<warning descr="Unknown string is used as safe parameter">fromAnotherFile2</warning>); //warn
|
||||
sink(<warning descr="Unknown string is used as safe parameter">fromAnotherFile3</warning>); //warn
|
||||
sink(<warning descr="Unknown string is used as safe parameter">fromAnotherFile4</warning>); //warn
|
||||
sink(fromAnotherFile5);
|
||||
sink(<warning descr="Unknown string is used as safe parameter">fromAnotherFile6</warning>); //warn
|
||||
String cleanLongString = "sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
clear +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh";
|
||||
sink(cleanLongString);
|
||||
sink(<warning descr="Unknown string is used as safe parameter">cleanLongString + dirty</warning>); //warn
|
||||
String dirtyLongString = "sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
dirty +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
clear +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh";
|
||||
sink(<warning descr="Unknown string is used as safe parameter">dirtyLongString</warning>); //warn
|
||||
|
||||
String a1 = clear + 1 + clear + clear + clear + clear + clear + clear + clear;
|
||||
String a2 = a1 + 1 + a1 + a1 + a1 + a1 + a1 + a1 + a1 + a1 + a1 + a1 + a1;
|
||||
String a3 = a2 + 1 + a2 + a2 + a2 + a2 + a2 + a2 + a2 + a2 + a2 + a2 + a2;
|
||||
String a4 = a3 + 1 + a3 + a3 + a3 + a3 + a3 + a3 + a3 + a3 + a3 + a3 + a3;
|
||||
String a5 = a4 + 1 + a4 + a4 + a4 + a4 + a4 + a4 + a4 + a4 + a4 + a4 + a4;
|
||||
String a6 = a5 + 1 + a5 + a5 + a5 + a5 + a5 + a5 + a5 + a5 + a5 + a5 + a5;
|
||||
String a7 = a6 + 1 + a6 + a6 + a6 + a6 + a6 + a6 + a6 + a6 + a6 + a6 + a6;
|
||||
String a8 = a7 + 1 + a7 + a7 + a7 + a7 + a7 + a7 + a7 + a7 + a7 + a7 + a7;
|
||||
String a9 = a8 + 1 + a8 + a8 + a8 + a8 + a8 + a8 + a8 + a8 + a8 + a8 + a8;
|
||||
String a10 = a9 + 1 + a9 + a9 + a9 + a9 + a9 + a9 + a9 + a9 + a9 + a9 + a9;
|
||||
sink(<weak_warning descr="Too complex to check that the string is safe in a safe context">a10</weak_warning>); //warn
|
||||
sink(a2);
|
||||
}
|
||||
|
||||
public static String next(String next) {
|
||||
return next;
|
||||
}
|
||||
|
||||
public static void sink(@Untainted String string) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import org.checkerframework.checker.tainting.qual.Untainted;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class LocalCheck {
|
||||
|
||||
|
||||
public void test(@Untainted List<String> clean, @Untainted List<String> cleanList2, @Untainted String t, String dirty) {
|
||||
sink(t);
|
||||
sink(clean.get(0));
|
||||
List<String> list1 = clean;
|
||||
List<String> list2 = clean;
|
||||
update(list1); //not highlighted in current realisation, might be changed
|
||||
list2.add(dirty); //not highlighted in current realisation, might be changed
|
||||
sink(<warning descr="Unknown string is used as safe parameter">list1.get(0)</warning>); //warn
|
||||
sink(<warning descr="Unknown string is used as safe parameter">list2.get(0)</warning>); //warn
|
||||
sink(clean.get(0));
|
||||
List<String> list3 = cleanList2;
|
||||
sink(list3.get(0));
|
||||
sink(<warning descr="Unknown string is used as safe parameter">dirty</warning>); //warn
|
||||
|
||||
String clean2 = t;
|
||||
sink(t);
|
||||
clean2 = dirty;
|
||||
sink(<warning descr="Unknown string is used as safe parameter">clean2</warning>); //warn
|
||||
|
||||
String toDirty = t;
|
||||
sink(toDirty);
|
||||
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
sink(toDirty);
|
||||
}
|
||||
};
|
||||
|
||||
Runnable runnable = () -> sink(toDirty);
|
||||
}
|
||||
|
||||
private void update(List<String> list) {
|
||||
}
|
||||
|
||||
public void sink(@Untainted String clean) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import org.checkerframework.checker.tainting.qual.Untainted;
|
||||
|
||||
public class MethodPropagation {
|
||||
|
||||
public void test1(String dirty, @Untainted String clean) {
|
||||
sink(<warning descr="Unknown string is used as safe parameter">dirty</warning>); //warn
|
||||
sink(clean);
|
||||
}
|
||||
|
||||
private String recursive(String dirty, @Untainted String clean) {
|
||||
if (clean == "") {
|
||||
String a = recursive(dirty, clean);
|
||||
sink(<warning descr="Unknown string is used as safe parameter">a</warning>); //warn
|
||||
return recursive(dirty, clean);
|
||||
}
|
||||
return recursive(clean, clean);
|
||||
}
|
||||
|
||||
|
||||
public void test2(String dirty, @Untainted String clean) {
|
||||
sink(<warning descr="Unknown string is used as safe parameter">next(dirty)</warning>); //warn
|
||||
sink(next(clean));
|
||||
|
||||
sink(<warning descr="Unknown string is used as safe parameter">nextPublic(dirty)</warning>); //warn
|
||||
sink(<warning descr="Unknown string is used as safe parameter">nextPublic(clean)</warning>); //warn (public)
|
||||
|
||||
sink(alwaysClean(dirty));
|
||||
sink(alwaysClean(clean));
|
||||
|
||||
sink(<warning descr="Unknown string is used as safe parameter">staticNext(dirty)</warning>); //warn
|
||||
sink(staticNext(clean));
|
||||
|
||||
sink(next(next(clean)));
|
||||
sink(next(next(next(next(next(next(next(next(clean)))))))));
|
||||
sink(<warning descr="Unknown string is used as safe parameter">next(next(next(next(next(next(next(next(dirty))))))))</warning>); //warn
|
||||
|
||||
sink(alwaysClean(next(next(next(next(next(next(next(clean)))))))));
|
||||
sink(alwaysClean(next(next(next(next(next(next(next(dirty)))))))));
|
||||
|
||||
sink(next(next(next(next(next(next(next(alwaysClean(clean)))))))));
|
||||
sink(next(next(next(next(next(next(next(alwaysClean(dirty)))))))));
|
||||
sink(next(alwaysClean(clean)));
|
||||
sink(next((alwaysClean(dirty))));
|
||||
|
||||
String alwaysClean = alwaysClean(next(next(next(next(next(next(next(clean))))))));
|
||||
sink(alwaysClean);
|
||||
String alwaysClean2 = alwaysClean(next(next(next(next(next(next(next(dirty))))))));
|
||||
sink(alwaysClean2);
|
||||
}
|
||||
|
||||
private String next(String next) {
|
||||
return next;
|
||||
}
|
||||
|
||||
public static String staticNext(String next) {
|
||||
return next;
|
||||
}
|
||||
|
||||
private String alwaysClean(String next) {
|
||||
return "next";
|
||||
}
|
||||
|
||||
public String nextPublic(String next) {
|
||||
return next;
|
||||
}
|
||||
|
||||
public static void sink(@Untainted String string) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import org.checkerframework.checker.tainting.qual.Untainted;
|
||||
|
||||
class MethodPropagation {
|
||||
|
||||
private String recursive(String dirty, @Untainted String clean) {
|
||||
if (clean == "") {
|
||||
String a = recursive(dirty,<error descr="Expression expected">)</error>;
|
||||
sink(<warning descr="Unknown string is used as safe parameter">a</warning>);
|
||||
return recursive(clean, clean);
|
||||
}
|
||||
return recursive(clean, clean);
|
||||
}
|
||||
|
||||
public static void sink(@Untainted String string) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import org.checkerframework.checker.tainting.qual.Untainted;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class SinkTest {
|
||||
|
||||
public void test(String string) {
|
||||
sink(<warning descr="Unknown string is used as safe parameter">string</warning>); //warn
|
||||
}
|
||||
|
||||
@Untainted
|
||||
public String returnDirty(String dirty) {
|
||||
return <warning descr="Unknown string is returned from safe method">dirty</warning>; //warn
|
||||
}
|
||||
|
||||
void sink(@Untainted String clear) {
|
||||
|
||||
}
|
||||
|
||||
void assignDirty(@Untainted String clear, String dirty) {
|
||||
clear = <warning descr="Unknown string is used as safe parameter">dirty</warning>; //warn
|
||||
}
|
||||
|
||||
@Untainted String dirty = <warning descr="Unknown string is used in a safe context">getFromStatic()</warning>; //warn
|
||||
|
||||
static List<String> list = new ArrayList<>();
|
||||
|
||||
private static String getFromStatic() {
|
||||
return list.get(0);
|
||||
}
|
||||
|
||||
@Untainted
|
||||
static String clear = "";
|
||||
|
||||
static void spoil(String dirty) {
|
||||
clear = <warning descr="Unknown string is used in a safe context">dirty</warning>; //warn
|
||||
}
|
||||
|
||||
static void testLocal(String dirty) {
|
||||
@Untainted String clean = <warning descr="Unknown string is assigned to safe variable">dirty</warning>; //warn
|
||||
}
|
||||
|
||||
static void testLocal2(String dirty) {
|
||||
@Untainted String clean = "";
|
||||
clean = <warning descr="Unknown string is assigned to safe variable">dirty</warning>; //warn
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import javax.annotation.Untainted;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class SinkTest {
|
||||
|
||||
public void test(String string) {
|
||||
sink(<warning descr="Unknown string is used as safe parameter">string</warning>); //warn
|
||||
}
|
||||
|
||||
@Untainted
|
||||
public String returnDirty(String dirty) {
|
||||
return <warning descr="Unknown string is returned from safe method">dirty</warning>; //warn
|
||||
}
|
||||
|
||||
void sink(@Untainted String clear) {
|
||||
|
||||
}
|
||||
|
||||
void assignDirty(@Untainted String clear, String dirty) {
|
||||
clear = <warning descr="Unknown string is used as safe parameter">dirty</warning>; //warn
|
||||
}
|
||||
|
||||
@Untainted String dirty = <warning descr="Unknown string is used in a safe context">getFromStatic()</warning>; //warn
|
||||
|
||||
static List<String> list = new ArrayList<>();
|
||||
|
||||
private static String getFromStatic() {
|
||||
return list.get(0);
|
||||
}
|
||||
|
||||
@Untainted
|
||||
static String clear = "";
|
||||
|
||||
static void spoil(String dirty) {
|
||||
clear = <warning descr="Unknown string is used in a safe context">dirty</warning>; //warn
|
||||
}
|
||||
|
||||
static void testLocal(String dirty) {
|
||||
@Untainted String clean = <warning descr="Unknown string is assigned to safe variable">dirty</warning>; //warn
|
||||
}
|
||||
|
||||
static void testLocal2(String dirty) {
|
||||
@Untainted String clean = "";
|
||||
clean = <warning descr="Unknown string is assigned to safe variable">dirty</warning>; //warn
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import org.checkerframework.checker.tainting.qual.Untainted;
|
||||
|
||||
|
||||
class A {
|
||||
|
||||
void test(@Untainted String clean, String dirty) {
|
||||
sink(clean);
|
||||
sink(<warning descr="Unknown string is used as safe parameter">dirty</warning>); //warn
|
||||
String cleanFromIf;
|
||||
if (1 == 1) {
|
||||
cleanFromIf = clean;
|
||||
} else {
|
||||
cleanFromIf = "1";
|
||||
}
|
||||
sink(cleanFromIf);
|
||||
String dirtyFromIf;
|
||||
if (1 == 1) {
|
||||
dirtyFromIf = clean;
|
||||
} else {
|
||||
dirtyFromIf = dirty;
|
||||
}
|
||||
sink(<warning descr="Unknown string is used as safe parameter">dirtyFromIf</warning>); //warn
|
||||
sink(<warning descr="Unknown string is used as safe parameter">1 == 1 ? dirty : clean</warning>); //warn
|
||||
String cleanFromSwitch = switch (dirty) {
|
||||
default -> "1";
|
||||
};
|
||||
sink(cleanFromSwitch);
|
||||
String dirtyFromSwitch = switch (dirty) {
|
||||
default -> dirty;
|
||||
};
|
||||
sink(<warning descr="Unknown string is used as safe parameter">dirtyFromSwitch</warning>); //warn
|
||||
String cleanFromSwitchStatement;
|
||||
switch (dirty) {
|
||||
case "1":
|
||||
cleanFromSwitchStatement = "2";
|
||||
break;
|
||||
default:
|
||||
cleanFromSwitchStatement = "3";
|
||||
}
|
||||
sink(cleanFromSwitchStatement);
|
||||
String dirtyFromSwitchStatement;
|
||||
switch (dirty) {
|
||||
case "1":
|
||||
dirtyFromSwitchStatement = "2";
|
||||
break;
|
||||
default:
|
||||
dirtyFromSwitchStatement = dirty;
|
||||
}
|
||||
sink(<warning descr="Unknown string is used as safe parameter">dirtyFromSwitchStatement</warning>); //warn
|
||||
String cleanLoop = "1";
|
||||
while (true) {
|
||||
cleanLoop = "2";
|
||||
break;
|
||||
}
|
||||
sink(cleanLoop);
|
||||
String dirtyLoop = "1";
|
||||
while (true) {
|
||||
dirtyLoop = dirty;
|
||||
break;
|
||||
}
|
||||
sink(<warning descr="Unknown string is used as safe parameter">dirtyLoop</warning>); //warn
|
||||
}
|
||||
|
||||
void sink(@Untainted String s) {
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,12 @@ import com.intellij.codeInspection.tests.sourceToSink.SourceToSinkFlowInspection
|
||||
import com.intellij.jvm.analysis.JavaJvmAnalysisTestUtil
|
||||
import com.intellij.testFramework.TestDataPath
|
||||
|
||||
private const val inspectionPath = "/codeInspection/sourceToSinkFlow"
|
||||
private const val INSPECTION_PATH = "/codeInspection/sourceToSinkFlow"
|
||||
|
||||
@TestDataPath("\$CONTENT_ROOT/testData$inspectionPath")
|
||||
@TestDataPath("\$CONTENT_ROOT/testData$INSPECTION_PATH")
|
||||
class JavaSourceToSinkFlowInspectionTest : SourceToSinkFlowInspectionTestBase() {
|
||||
override fun getBasePath(): String {
|
||||
return JavaJvmAnalysisTestUtil.TEST_DATA_PROJECT_RELATIVE_BASE_PATH + inspectionPath
|
||||
return JavaJvmAnalysisTestUtil.TEST_DATA_PROJECT_RELATIVE_BASE_PATH + INSPECTION_PATH
|
||||
}
|
||||
|
||||
fun testSimple() {
|
||||
@@ -27,4 +27,77 @@ class JavaSourceToSinkFlowInspectionTest : SourceToSinkFlowInspectionTestBase()
|
||||
prepareJsr()
|
||||
myFixture.testHighlighting("JsrSimple.java")
|
||||
}
|
||||
|
||||
fun testSink() {
|
||||
prepareCheckFramework()
|
||||
myFixture.testHighlighting("Sink.java")
|
||||
}
|
||||
fun testSinkJsr() {
|
||||
prepareJsr()
|
||||
myFixture.testHighlighting("SinkJsr.java")
|
||||
}
|
||||
|
||||
fun testCall() {
|
||||
prepareCheckFramework()
|
||||
myFixture.testHighlighting("Call.java")
|
||||
}
|
||||
|
||||
fun testLocalVariables() {
|
||||
prepareCheckFramework()
|
||||
myFixture.testHighlighting("LocalVariable.java")
|
||||
}
|
||||
|
||||
fun testEnumAnnotations() {
|
||||
prepareCheckFramework()
|
||||
myFixture.testHighlighting("EnumAnnotations.java")
|
||||
}
|
||||
fun testFields() {
|
||||
prepareCheckFramework()
|
||||
myFixture.testHighlighting("Fields.java")
|
||||
}
|
||||
|
||||
fun testStructure() {
|
||||
prepareCheckFramework()
|
||||
myFixture.testHighlighting("Structure.java")
|
||||
}
|
||||
|
||||
fun testRecursive() {
|
||||
prepareCheckFramework()
|
||||
myFixture.testHighlighting("Recursive.java")
|
||||
}
|
||||
|
||||
fun testMethodPropagation() {
|
||||
prepareCheckFramework()
|
||||
myFixture.testHighlighting("MethodPropagation.java")
|
||||
}
|
||||
|
||||
fun testDifferentExpression() {
|
||||
prepareCheckFramework()
|
||||
myFixture.testHighlighting("DifferentExpression.java")
|
||||
}
|
||||
|
||||
fun testLimits() {
|
||||
prepareCheckFramework()
|
||||
myFixture.addClass("""
|
||||
@SuppressWarnings({"FieldMayBeStatic", "StaticNonFinalField", "RedundantSuppression"})
|
||||
public class Limit2 {
|
||||
|
||||
public final static String fromAnotherFile = "1";
|
||||
public final static String fromAnotherFile2 = fromAnotherFile;
|
||||
public final static String fromAnotherFile3 = fromMethod();
|
||||
public static String fromAnotherFile4 = "1";
|
||||
public final String fromAnotherFile5 = "1";
|
||||
public final String fromAnotherFile6;
|
||||
|
||||
public Limit2() {
|
||||
this.fromAnotherFile6 = "";
|
||||
}
|
||||
|
||||
private static String fromMethod() {
|
||||
return "null";
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
myFixture.testHighlighting("Limits.java")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import org.checkerframework.checker.tainting.qual.Untainted
|
||||
|
||||
class CallsCheck {
|
||||
fun testCall(dirty: String, clean: @Untainted String) {
|
||||
sink(<warning descr="Unknown string is used as safe parameter">dirty</warning>) //warn
|
||||
sink("")
|
||||
sink(cleanMethod())
|
||||
sink(<warning descr="Unknown string is used as safe parameter">publicMethod()</warning>) //warn
|
||||
sink(publicFinalMethod())
|
||||
sink(<warning descr="Unknown string is used as safe parameter">privateDirty(dirty)</warning>) //warn
|
||||
sink(clean)
|
||||
}
|
||||
|
||||
private fun privateDirty(dirty: String): String {
|
||||
return dirty
|
||||
}
|
||||
|
||||
<warning descr="[NON_FINAL_MEMBER_IN_FINAL_CLASS] 'open' has no effect in a final class">open</warning> fun publicMethod(): String {
|
||||
return "1"
|
||||
}
|
||||
|
||||
fun publicFinalMethod(): String {
|
||||
return "1"
|
||||
}
|
||||
|
||||
private fun cleanMethod(): String {
|
||||
return "null"
|
||||
}
|
||||
|
||||
fun sink(<warning descr="[UNUSED_PARAMETER] Parameter 'clean' is never used">clean</warning>: @Untainted String?) {}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import org.checkerframework.checker.tainting.qual.Untainted
|
||||
|
||||
class DifferentExpression {
|
||||
fun test() {
|
||||
sink(this.toString())
|
||||
val r = Runnable {}
|
||||
sink(<warning descr="Unknown string is used as safe parameter">r.toString()</warning>) //warn
|
||||
sink(DifferentExpression::class.toString())
|
||||
sink("test" + (1 - 1))
|
||||
var x = 1
|
||||
sink("test" + ++x)
|
||||
sink(param2(<error descr="[NO_VALUE_FOR_PARAMETER] No value passed for parameter 't1'">"1", )</error>) //warn
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun sink(<warning descr="[UNUSED_PARAMETER] Parameter 'string' is never used">string</warning>: @Untainted String?) {}
|
||||
fun param2(<warning descr="[UNUSED_PARAMETER] Parameter 't' is never used">t</warning>: String?, t1: String): String {
|
||||
return t1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
@file:Suppress("UNUSED_PARAMETER")
|
||||
|
||||
import org.checkerframework.checker.tainting.qual.Untainted
|
||||
|
||||
internal class LocalCheck {
|
||||
enum class State {
|
||||
OFF,
|
||||
ON
|
||||
}
|
||||
|
||||
annotation class InterfaceSomething
|
||||
|
||||
fun test(clean: @Untainted String?, dirty: String?, state: State, interfaceSomething: InterfaceSomething) {
|
||||
sink(clean)
|
||||
sink(<warning descr="Unknown string is used as safe parameter">dirty</warning>) //warn
|
||||
sink(state.name)
|
||||
sink(interfaceSomething.toString())
|
||||
}
|
||||
|
||||
fun sink(clean: @Untainted String?) {}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
|
||||
import org.checkerframework.checker.tainting.qual.Untainted
|
||||
|
||||
val cleanOuter = "2"
|
||||
var notCleanOuter = "2"
|
||||
|
||||
class FieldsCheck(val property1: String, private val property2: String) {
|
||||
val constant = "1"
|
||||
private val clean = "1"
|
||||
private var notClean = "1"
|
||||
private var clean3 = "1"
|
||||
private val clean2 = "2"
|
||||
fun setNotClean(notClean: String) {
|
||||
this.notClean = notClean
|
||||
}
|
||||
|
||||
companion object {
|
||||
val cleanOuter2 = "2"
|
||||
var notCleanOuter2 = "2"
|
||||
}
|
||||
|
||||
fun test() {
|
||||
sink(constant)
|
||||
sink(clean)
|
||||
sink(clean2)
|
||||
sink(clean3)
|
||||
sink(<warning descr="Unknown string is used as safe parameter">notClean</warning>) //warn
|
||||
sink(<warning descr="Unknown string is used as safe parameter">property1</warning>) //warn
|
||||
sink(<warning descr="Unknown string is used as safe parameter">property2</warning>) //warn
|
||||
sink(cleanOuter)
|
||||
sink(<warning descr="Unknown string is used as safe parameter">notCleanOuter</warning>) //warn
|
||||
sink(cleanOuter2)
|
||||
sink(<warning descr="Unknown string is used as safe parameter">notCleanOuter2</warning>) //warn
|
||||
}
|
||||
|
||||
fun sink(<warning descr="[UNUSED_PARAMETER] Parameter 's' is never used">s</warning>: @Untainted String?) {}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
import org.checkerframework.checker.tainting.qual.Untainted
|
||||
|
||||
object Limit {
|
||||
const val fromAnotherFile = Limit2.fromAnotherFile
|
||||
const val fromAnotherFile2 = Limit2.fromAnotherFile2
|
||||
val fromAnotherFile3 = Limit2.fromAnotherFile3
|
||||
val fromAnotherFile4 = Limit2.fromAnotherFile4
|
||||
val fromAnotherFile5 = Limit2().fromAnotherFile5
|
||||
val fromAnotherFile6 = Limit2().fromAnotherFile6
|
||||
fun test(clear: @Untainted String?, dirty: String) {
|
||||
sink(<warning descr="Unknown string is used as safe parameter">dirty</warning>) //warn
|
||||
sink(<warning descr="Unknown string is used as safe parameter">next(next(next(next(next(next(next(next(next(next(next(next(next(next(next(dirty)))))))))))))))</warning>) //warn
|
||||
sink(<warning descr="Unknown string is used as safe parameter">next(next(next(next(next(next(next(dirty)))))))</warning>) //warn
|
||||
val nextVariable = next(next(next(next(next(next(next(clear)))))))
|
||||
sink(nextVariable)
|
||||
sink(nextVariable + nextVariable + nextVariable + nextVariable + nextVariable + nextVariable)
|
||||
sink(<warning descr="Unknown string is used as safe parameter">nextVariable + next(next(next(next(next(next(next(dirty)))))))</warning>) //warn
|
||||
sink(<warning descr="Unknown string is used as safe parameter">"${dirty} test dirty"</warning>) //warn
|
||||
sink("$clear test clear")
|
||||
sink(fromAnotherFile)
|
||||
sink(<warning descr="Unknown string is used as safe parameter">fromAnotherFile2</warning>) //warn
|
||||
sink(<warning descr="Unknown string is used as safe parameter">fromAnotherFile3</warning>) //warn
|
||||
sink(<warning descr="Unknown string is used as safe parameter">fromAnotherFile4</warning>) //warn
|
||||
sink(fromAnotherFile5)
|
||||
sink(<warning descr="Unknown string is used as safe parameter">fromAnotherFile6</warning>) //warn
|
||||
val cleanLongString = "sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
clear +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh"
|
||||
sink(cleanLongString)
|
||||
sink(<warning descr="Unknown string is used as safe parameter">cleanLongString + dirty</warning>) //warn
|
||||
val dirtyLongString = "sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
dirty +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
clear +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh" +
|
||||
"sdafjhasdfkhaskjdfh"
|
||||
sink( <warning descr="Unknown string is used as safe parameter">dirtyLongString</warning>) //warn
|
||||
val a1 = clear + 1 + clear + clear + clear + clear + clear + clear + clear
|
||||
val a2 = a1 + 1 + a1 + a1 + a1 + a1 + a1 + a1 + a1 + a1 + a1 + a1 + a1
|
||||
val a3 = a2 + 1 + a2 + a2 + a2 + a2 + a2 + a2 + a2 + a2 + a2 + a2 + a2
|
||||
val a4 = a3 + 1 + a3 + a3 + a3 + a3 + a3 + a3 + a3 + a3 + a3 + a3 + a3
|
||||
val a5 = a4 + 1 + a4 + a4 + a4 + a4 + a4 + a4 + a4 + a4 + a4 + a4 + a4
|
||||
val a6 = a5 + 1 + a5 + a5 + a5 + a5 + a5 + a5 + a5 + a5 + a5 + a5 + a5
|
||||
val a7 = a6 + 1 + a6 + a6 + a6 + a6 + a6 + a6 + a6 + a6 + a6 + a6 + a6
|
||||
val a8 = a7 + 1 + a7 + a7 + a7 + a7 + a7 + a7 + a7 + a7 + a7 + a7 + a7
|
||||
val a9 = a8 + 1 + a8 + a8 + a8 + a8 + a8 + a8 + a8 + a8 + a8 + a8 + a8
|
||||
val a10 = a9 + 1 + a9 + a9 + a9 + a9 + a9 + a9 + a9 + a9 + a9 + a9 + a9
|
||||
sink(<weak_warning descr="Too complex to check that the string is safe in a safe context">a10</weak_warning>) //warn
|
||||
sink(a2)
|
||||
}
|
||||
|
||||
fun next(next: String?): String? {
|
||||
return next
|
||||
}
|
||||
|
||||
fun sink(<warning descr="[UNUSED_PARAMETER] Parameter 'string' is never used">string</warning>: @Untainted String?) {}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.checkerframework.checker.tainting.qual
|
||||
import org.checkerframework.checker.tainting.qual.Untainted
|
||||
import org.checkerframework.checker.tainting.qual.Tainted
|
||||
|
||||
class LocalInference {
|
||||
open class LocalInference {
|
||||
fun simpleInit() {
|
||||
val s1 = source()
|
||||
val s = s1
|
||||
@@ -16,7 +17,7 @@ class LocalInference {
|
||||
fun recursive() {
|
||||
var s1 = source()
|
||||
val s = s1
|
||||
<warning descr="[UNUSED_VALUE] The value 's' assigned to 'var s1: String defined in org.checkerframework.checker.tainting.qual.LocalInference.recursive' is never used">s1 =</warning> s
|
||||
<warning descr="[UNUSED_VALUE] The value 's' assigned to 'var s1: String defined in LocalInference.recursive' is never used">s1 =</warning> s
|
||||
sink(<warning descr="Unsafe string is used as safe parameter">s</warning>)
|
||||
}
|
||||
|
||||
@@ -47,7 +48,7 @@ class LocalInference {
|
||||
sink(<warning descr="Unknown string is used as safe parameter">s</warning>)
|
||||
}
|
||||
|
||||
fun foo(): String {
|
||||
open fun foo(): String {
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
@file:Suppress("UNUSED_VARIABLE", "UNUSED_PARAMETER")
|
||||
|
||||
import org.checkerframework.checker.tainting.qual.Untainted
|
||||
|
||||
class LocalCheck {
|
||||
fun test(clean: @Untainted MutableList<String?>, cleanList2: @Untainted MutableList<String>, t: @Untainted String?, dirty: String?) {
|
||||
sink(t)
|
||||
sink(clean[0])
|
||||
val list1: List<String?> = clean
|
||||
update(list1) //not highlighted in current realisation, might be changed
|
||||
clean.add(dirty) //not highlighted in current realisation, might be changed
|
||||
sink(<warning descr="Unknown string is used as safe parameter">list1[0]</warning>) //warn
|
||||
sink(clean[0]) //warn
|
||||
val list3: List<String> = cleanList2
|
||||
sink(list3[0])
|
||||
sink(<warning descr="Unknown string is used as safe parameter">dirty</warning>) //warn
|
||||
var clean2 = t + dirty
|
||||
sink(<warning descr="Unknown string is used as safe parameter">clean2</warning>) // warn
|
||||
var newT: String?
|
||||
newT = t
|
||||
sink(newT)
|
||||
val runnable = Runnable {
|
||||
sink(<warning descr="Unknown string is used as safe parameter">newT</warning>) //warn
|
||||
}
|
||||
val runnable2: () -> Unit = { sink(<warning descr="Unknown string is used as safe parameter">newT</warning>) } //warn
|
||||
newT = dirty
|
||||
}
|
||||
|
||||
private fun update(list: List<String?>?) {}
|
||||
fun sink(clean: @Untainted String?) {}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import org.checkerframework.checker.tainting.qual.Untainted
|
||||
|
||||
open class MethodPropagation {
|
||||
fun test1(dirty: String?, clean: @Untainted String?) {
|
||||
|
||||
sink(<warning descr="Unknown string is used as safe parameter">dirty</warning>) //warn
|
||||
sink(clean)
|
||||
}
|
||||
|
||||
private fun recursive(dirty: String?, clean: @Untainted String?): String {
|
||||
if (clean === "") {
|
||||
val a = recursive(dirty, clean)
|
||||
sink(<warning descr="Unknown string is used as safe parameter">a</warning>) //warn
|
||||
return recursive(dirty, clean)
|
||||
}
|
||||
return recursive(clean, clean)
|
||||
}
|
||||
|
||||
fun test2(dirty: String?, clean: @Untainted String?) {
|
||||
sink(<warning descr="Unknown string is used as safe parameter">next(dirty)</warning>) //warn
|
||||
sink(next(clean))
|
||||
sink(<warning descr="Unknown string is used as safe parameter">nextPublic(dirty)</warning>) //warn
|
||||
sink(<warning descr="Unknown string is used as safe parameter">nextPublic(clean)</warning>) //warn (public)
|
||||
sink(alwaysClean(dirty))
|
||||
sink(alwaysClean(clean))
|
||||
sink(<warning descr="Unknown string is used as safe parameter">staticNext(dirty)</warning>) //warn
|
||||
sink(staticNext(clean))
|
||||
sink(next(next(clean)))
|
||||
sink(next(next(next(next(next(next(next(next(clean)))))))))
|
||||
sink(<warning descr="Unknown string is used as safe parameter">next(next(next(next(next(next(next(next(dirty))))))))</warning>) //warn
|
||||
sink(alwaysClean(next(next(next(next(next(next(next(clean)))))))))
|
||||
sink(alwaysClean(next(next(next(next(next(next(next(dirty)))))))))
|
||||
sink(next(next(next(next(next(next(next(alwaysClean(clean)))))))))
|
||||
sink(next(next(next(next(next(next(next(alwaysClean(dirty)))))))))
|
||||
sink(next(alwaysClean(clean)))
|
||||
sink(next(alwaysClean(dirty)))
|
||||
val alwaysClean = alwaysClean(next(next(next(next(next(next(next(clean))))))))
|
||||
sink(alwaysClean)
|
||||
val alwaysClean2 = alwaysClean(next(next(next(next(next(next(next(dirty))))))))
|
||||
sink(alwaysClean2)
|
||||
}
|
||||
|
||||
private fun next(next: String?): String? {
|
||||
return next
|
||||
}
|
||||
|
||||
private fun alwaysClean(<warning descr="[UNUSED_PARAMETER] Parameter 'next' is never used">next</warning>: String?): String {
|
||||
return "next"
|
||||
}
|
||||
|
||||
open fun nextPublic(next: String?): String? {
|
||||
return next
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun staticNext(next: String?): String? {
|
||||
return next
|
||||
}
|
||||
|
||||
fun sink(<warning descr="[UNUSED_PARAMETER] Parameter 'string' is never used">string</warning>: @Untainted String?) {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import org.checkerframework.checker.tainting.qual.Untainted
|
||||
|
||||
class MethodAsFieldTest {
|
||||
|
||||
fun test(clean: @Untainted MethodAsFields, unclean: MethodAsFields) {
|
||||
sink(clean.t)
|
||||
sink(<warning descr="Unknown string is used as safe parameter">unclean.t</warning>) //warn
|
||||
}
|
||||
|
||||
fun sink(<warning descr="[UNUSED_PARAMETER] Parameter 'string' is never used">string</warning>: @Untainted String?) {}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
@file:Suppress("UNUSED_PARAMETER")
|
||||
|
||||
import org.checkerframework.checker.tainting.qual.Tainted
|
||||
import org.checkerframework.checker.tainting.qual.Untainted
|
||||
|
||||
class LocalCheck {
|
||||
fun test(clean: @Untainted String = <warning descr="Unsafe string is used as safe parameter">dirty()</warning>) {
|
||||
}
|
||||
|
||||
private fun dirty(): @Tainted String {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
package org.checkerframework.checker.tainting.qual
|
||||
import org.checkerframework.checker.tainting.qual.Untainted
|
||||
import org.checkerframework.checker.tainting.qual.Tainted
|
||||
|
||||
class Simple {
|
||||
fun simple() {
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
@file:Suppress("unused", "UNUSED_PARAMETER", "UNUSED_VARIABLE", "ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE", "UNUSED_VALUE")
|
||||
|
||||
import org.checkerframework.checker.tainting.qual.Tainted
|
||||
import org.checkerframework.checker.tainting.qual.Untainted
|
||||
|
||||
val dirty: @Tainted String = ""
|
||||
|
||||
var clean: @Untainted String = <warning descr="Unsafe string is used in a safe context">dirty</warning> //warn
|
||||
|
||||
var clean2: @Untainted String = ""
|
||||
|
||||
class SinkTestKotlin {
|
||||
|
||||
fun breakClean2(dirty: String) {
|
||||
clean2 = <warning descr="Unknown string is returned from safe method">dirty</warning> // warn
|
||||
}
|
||||
|
||||
companion object {
|
||||
val dirty: @Tainted String = ""
|
||||
|
||||
var clean: @Untainted String = <warning descr="Unsafe string is used in a safe context">dirty</warning> //warn
|
||||
|
||||
var clean2: @Untainted String = ""
|
||||
|
||||
fun breakClean2(dirty: String) {
|
||||
clean2 = <warning descr="Unknown string is returned from safe method">dirty</warning> // warn
|
||||
}
|
||||
}
|
||||
|
||||
fun test(string: String?) {
|
||||
sink(<warning descr="Unknown string is used as safe parameter">string</warning>) //warn
|
||||
}
|
||||
|
||||
fun returnDirty(dirty: String?): @Untainted String? {
|
||||
return <warning descr="Unknown string is returned from safe method">dirty</warning> //warn
|
||||
}
|
||||
|
||||
fun sink(clear: @Untainted String?) {
|
||||
}
|
||||
|
||||
fun assignDirty(clear: @Untainted String?, dirty: String?) {
|
||||
var clear1 = clear
|
||||
var clear2: String? = clear1
|
||||
clear1 = <warning descr="Unknown string is assigned to safe variable">dirty</warning> //warn
|
||||
clear2 = dirty
|
||||
}
|
||||
|
||||
var dirty: @Untainted String? = <warning descr="Unsafe string is used in a safe context">getFromStatic()</warning> //warn
|
||||
|
||||
private fun getFromStatic(): @Tainted String {
|
||||
return ""
|
||||
}
|
||||
|
||||
var clear: @Untainted String? = ""
|
||||
|
||||
fun spoil(dirty: String?) {
|
||||
clear = <warning descr="Unknown string is returned from safe method">dirty</warning> //warn
|
||||
}
|
||||
|
||||
fun testLocal(dirty: String?) {
|
||||
val clean: @Untainted String? = <warning descr="Unknown string is assigned to safe variable">dirty</warning> //warn
|
||||
}
|
||||
|
||||
fun testParameter(clean: @Untainted String = <warning descr="Unsafe string is used as safe parameter">getDirty()</warning>) { //warn
|
||||
|
||||
}
|
||||
|
||||
fun getDirty(): @Tainted String = ""
|
||||
|
||||
fun testLocal2(dirty: String?) {
|
||||
var clean: @Untainted String? = ""
|
||||
clean = <warning descr="Unknown string is assigned to safe variable">dirty</warning> //warn
|
||||
}
|
||||
|
||||
fun println(t: String): String {
|
||||
return t
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
@file:Suppress("unused", "UNUSED_PARAMETER", "UNUSED_VARIABLE", "ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE", "UNUSED_VALUE")
|
||||
|
||||
import javax.annotation.Tainted
|
||||
import javax.annotation.Untainted
|
||||
|
||||
|
||||
@Tainted
|
||||
val dirty: String = ""
|
||||
|
||||
@Untainted
|
||||
var clean: String = <warning descr="Unsafe string is used in a safe context">dirty</warning> //warn
|
||||
|
||||
@Untainted
|
||||
var clean2: String = ""
|
||||
|
||||
class SinkTestKotlin {
|
||||
|
||||
fun breakClean2(dirty: String) {
|
||||
clean2 = <warning descr="Unknown string is returned from safe method">dirty</warning> // warn
|
||||
}
|
||||
|
||||
companion object {
|
||||
@Tainted
|
||||
val dirty: String = ""
|
||||
|
||||
@Untainted
|
||||
var clean: String = <warning descr="Unsafe string is used in a safe context">dirty</warning> //warn
|
||||
|
||||
@Untainted
|
||||
var clean2: String = ""
|
||||
|
||||
fun breakClean2(dirty: String) {
|
||||
clean2 = <warning descr="Unknown string is returned from safe method">dirty</warning> // warn
|
||||
}
|
||||
}
|
||||
|
||||
fun test(string: String?) {
|
||||
sink(<warning descr="Unknown string is used as safe parameter">string</warning>) //warn
|
||||
}
|
||||
|
||||
@Untainted
|
||||
fun returnDirty(dirty: String?): String? {
|
||||
return <warning descr="Unknown string is returned from safe method">dirty</warning> //warn
|
||||
}
|
||||
|
||||
|
||||
fun sink(@Untainted clear: String?) {
|
||||
println(clear!!)
|
||||
}
|
||||
|
||||
fun assignDirty(@Untainted clear: String?, dirty: String?) {
|
||||
@Untainted var clear1: String? = clear
|
||||
var clear2: String? = clear1
|
||||
clear1 = <warning descr="Unknown string is assigned to safe variable">dirty</warning> //warn
|
||||
clear2 = dirty
|
||||
}
|
||||
|
||||
@Untainted
|
||||
var dirty: String? = <warning descr="Unsafe string is used in a safe context">getFromStatic()</warning> //warn
|
||||
|
||||
@Tainted
|
||||
private fun getFromStatic(): String {
|
||||
return ""
|
||||
}
|
||||
|
||||
@Untainted
|
||||
var clear: String? = ""
|
||||
|
||||
fun spoil(dirty: String?) {
|
||||
clear = <warning descr="Unknown string is returned from safe method">dirty</warning> //warn
|
||||
}
|
||||
|
||||
fun testLocal(dirty: String?) {
|
||||
@Untainted val clean: String? = <warning descr="Unknown string is assigned to safe variable">dirty</warning> //warn
|
||||
}
|
||||
|
||||
fun testParameter(@Untainted clean: String = <warning descr="Unsafe string is used as safe parameter">getDirty()</warning>) { //warn
|
||||
|
||||
}
|
||||
|
||||
@Tainted
|
||||
fun getDirty(): String = ""
|
||||
|
||||
fun testLocal2(dirty: String?) {
|
||||
@Untainted var clean: String? = ""
|
||||
clean = <warning descr="Unknown string is assigned to safe variable">dirty</warning> //warn
|
||||
}
|
||||
|
||||
fun println(t: String): String {
|
||||
return t
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
@file:Suppress("unused", "UNUSED_PARAMETER", "UNUSED_VARIABLE", "ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE", "UNUSED_VALUE")
|
||||
|
||||
import org.checkerframework.checker.tainting.qual.Untainted
|
||||
|
||||
class StructureTest {
|
||||
fun test(clean: @Untainted String, unclean: String) {
|
||||
sink(<warning descr="Unknown string is used as safe parameter">unclean</warning>) //warn
|
||||
sink(clean)
|
||||
|
||||
sink(<warning descr="Unknown string is used as safe parameter">if (1 == 1) unclean else clean</warning>) //warn
|
||||
sink(if (1 == 1) clean else clean)
|
||||
|
||||
val s = if (1 == 1) unclean else clean
|
||||
sink(<warning descr="Unknown string is used as safe parameter">s</warning>) //warn
|
||||
|
||||
sink(<warning descr="Unknown string is used as safe parameter">when {
|
||||
1 == 1 -> clean
|
||||
else -> unclean
|
||||
}</warning>) //warn
|
||||
|
||||
sink(when {
|
||||
1 == 1 -> clean
|
||||
else -> clean
|
||||
})
|
||||
|
||||
val s1: String = when {
|
||||
1 == 1 -> clean
|
||||
else -> unclean
|
||||
}
|
||||
|
||||
sink(<warning descr="Unknown string is used as safe parameter">s1</warning>) //warn
|
||||
|
||||
var sumDirty: String = clean
|
||||
var sumClean = clean
|
||||
while (true) {
|
||||
sumDirty += unclean
|
||||
sumClean += clean
|
||||
break
|
||||
}
|
||||
sink(<warning descr="Unknown string is used as safe parameter">sumDirty</warning>) //warn
|
||||
sink(sumClean)
|
||||
}
|
||||
|
||||
fun sink(s: @Untainted String?) {}
|
||||
}
|
||||
@@ -1,16 +1,9 @@
|
||||
import org.checkerframework.checker.tainting.qual.Untainted
|
||||
|
||||
val sOuterField: String = getSomething()
|
||||
var sOuterField2: @Untainted String = getSomething()
|
||||
|
||||
fun getSomething(): String {
|
||||
return "1"
|
||||
}
|
||||
|
||||
internal class CommonCases {
|
||||
public val sField: String? = null
|
||||
private fun test(s: @Untainted String): @Untainted String {
|
||||
val s1 = s + getS(s) + sField + sOuterField + sOuterField2 + "1".extFunc() + "1".extFunc2(s) + comObject + comObject2
|
||||
val s1 = s + getS(s) + sField + "1".extFunc() + "1".extFunc2(s) + comObject2
|
||||
return <caret>s1
|
||||
}
|
||||
|
||||
@@ -19,7 +12,6 @@ internal class CommonCases {
|
||||
}
|
||||
|
||||
companion object{
|
||||
val comObject = getSomething2()
|
||||
var comObject2: @Untainted String = getSomething2()
|
||||
|
||||
private fun getSomething2(): String {
|
||||
@@ -29,4 +21,4 @@ internal class CommonCases {
|
||||
}
|
||||
|
||||
private fun String.extFunc() = "test"
|
||||
private fun String.extFunc2(s: @Untainted String): @Untainted @Untainted String = s
|
||||
private fun String.extFunc2(s: String): @Untainted String = s
|
||||
|
||||
@@ -1,16 +1,9 @@
|
||||
import org.checkerframework.checker.tainting.qual.Untainted
|
||||
|
||||
val sOuterField: String = getSomething()
|
||||
var sOuterField2: String = getSomething()
|
||||
|
||||
fun getSomething(): String {
|
||||
return "1"
|
||||
}
|
||||
|
||||
internal class CommonCases {
|
||||
public val sField: String? = null
|
||||
private fun test(s: String): @Untainted String {
|
||||
val s1 = s + getS(s) + sField + sOuterField + sOuterField2 + "1".extFunc() + "1".extFunc2(s) + comObject + comObject2
|
||||
val s1 = s + getS(s) + sField + "1".extFunc() + "1".extFunc2(s) + comObject2
|
||||
return <caret>s1
|
||||
}
|
||||
|
||||
@@ -19,7 +12,6 @@ internal class CommonCases {
|
||||
}
|
||||
|
||||
companion object{
|
||||
val comObject = getSomething2()
|
||||
var comObject2 = getSomething2()
|
||||
|
||||
private fun getSomething2(): String {
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import javax.annotation.Untainted
|
||||
|
||||
val sOuterField: String = getSomething()
|
||||
@get:Untainted
|
||||
var sOuterField2: String = getSomething()
|
||||
|
||||
fun getSomething(): String {
|
||||
return "1"
|
||||
}
|
||||
|
||||
internal class CommonCases {
|
||||
public val sField: String? = null
|
||||
private fun test(@Untainted s: String): @Untainted String {
|
||||
val s1 = s + getS(s) + sField + sOuterField + sOuterField2 + "1".extFunc() + "1".extFunc2(s) + comObject + comObject2
|
||||
|
||||
@Untainted
|
||||
private fun test(@Untainted s: String): String {
|
||||
val s1 = s + getS(s) + sField + "1".extFunc() + "1".extFunc2(s) + comObject2
|
||||
return <caret>s1
|
||||
}
|
||||
|
||||
@@ -21,8 +15,7 @@ internal class CommonCases {
|
||||
}
|
||||
|
||||
companion object{
|
||||
val comObject = getSomething2()
|
||||
@get:Untainted
|
||||
@field:Untainted
|
||||
var comObject2 = getSomething2()
|
||||
|
||||
private fun getSomething2(): String {
|
||||
@@ -33,4 +26,4 @@ internal class CommonCases {
|
||||
|
||||
private fun String.extFunc() = "test"
|
||||
@Untainted
|
||||
private fun String.extFunc2(@Untainted s: String) = s
|
||||
private fun String.extFunc2(s: String) = s
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
import javax.annotation.Untainted
|
||||
|
||||
val sOuterField: String = getSomething()
|
||||
var sOuterField2: String = getSomething()
|
||||
|
||||
fun getSomething(): String {
|
||||
return "1"
|
||||
}
|
||||
|
||||
internal class CommonCases {
|
||||
public val sField: String? = null
|
||||
private fun test(s: String): @Untainted String {
|
||||
val s1 = s + getS(s) + sField + sOuterField + sOuterField2 + "1".extFunc() + "1".extFunc2(s) + comObject + comObject2
|
||||
|
||||
@Untainted
|
||||
private fun test(s: String): String {
|
||||
val s1 = s + getS(s) + sField + "1".extFunc() + "1".extFunc2(s) + comObject2
|
||||
return <caret>s1
|
||||
}
|
||||
|
||||
@@ -19,7 +14,6 @@ internal class CommonCases {
|
||||
}
|
||||
|
||||
companion object{
|
||||
val comObject = getSomething2()
|
||||
var comObject2 = getSomething2()
|
||||
|
||||
private fun getSomething2(): String {
|
||||
|
||||
@@ -5,11 +5,11 @@ import com.intellij.codeInspection.tests.sourceToSink.SourceToSinkFlowInspection
|
||||
import com.intellij.jvm.analysis.KotlinJvmAnalysisTestUtil
|
||||
import com.intellij.testFramework.TestDataPath
|
||||
|
||||
private const val inspectionPath = "/codeInspection/sourceToSinkFlow"
|
||||
private const val INSPECTION_PATH = "/codeInspection/sourceToSinkFlow"
|
||||
|
||||
@TestDataPath("\$CONTENT_ROOT/testData$inspectionPath")
|
||||
@TestDataPath("\$CONTENT_ROOT/testData$INSPECTION_PATH")
|
||||
class KotlinSourceToSinkFlowInspectionTest : SourceToSinkFlowInspectionTestBase() {
|
||||
override fun getBasePath() = KotlinJvmAnalysisTestUtil.TEST_DATA_PROJECT_RELATIVE_BASE_PATH + inspectionPath
|
||||
override fun getBasePath() = KotlinJvmAnalysisTestUtil.TEST_DATA_PROJECT_RELATIVE_BASE_PATH + INSPECTION_PATH
|
||||
|
||||
fun testSimple() {
|
||||
prepareCheckFramework()
|
||||
@@ -21,11 +21,101 @@ class KotlinSourceToSinkFlowInspectionTest : SourceToSinkFlowInspectionTestBase(
|
||||
myFixture.testHighlighting("LocalInference.kt")
|
||||
}
|
||||
|
||||
fun testSink() {
|
||||
prepareCheckFramework()
|
||||
myFixture.testHighlighting("Sink.kt")
|
||||
}
|
||||
|
||||
fun testSinkJsr() {
|
||||
prepareJsr()
|
||||
myFixture.testHighlighting("SinkJsr.kt")
|
||||
}
|
||||
|
||||
fun testCall() {
|
||||
prepareCheckFramework()
|
||||
myFixture.testHighlighting("Call.kt")
|
||||
}
|
||||
|
||||
fun testLocalVariable() {
|
||||
prepareCheckFramework()
|
||||
myFixture.testHighlighting("LocalVariables.kt")
|
||||
}
|
||||
|
||||
fun testParameters() {
|
||||
prepareCheckFramework()
|
||||
myFixture.testHighlighting("Parameters.kt")
|
||||
}
|
||||
|
||||
fun testEnumAnnotations() {
|
||||
prepareCheckFramework()
|
||||
myFixture.testHighlighting("EnumAnnotations.kt")
|
||||
}
|
||||
|
||||
fun testFields() {
|
||||
prepareCheckFramework()
|
||||
myFixture.testHighlighting("Fields.kt")
|
||||
}
|
||||
|
||||
fun testStructure() {
|
||||
prepareCheckFramework()
|
||||
myFixture.testHighlighting("Structure.kt")
|
||||
}
|
||||
|
||||
fun testMethodPropagation() {
|
||||
prepareCheckFramework()
|
||||
myFixture.testHighlighting("MethodPropagation.kt")
|
||||
}
|
||||
|
||||
fun testKotlinPropertyPropagateFix() {
|
||||
prepareCheckFramework()
|
||||
myFixture.configureByFile("Property.kt")
|
||||
val propagateAction = myFixture.getAvailableIntention(JvmAnalysisBundle.message("jvm.inspections.source.unsafe.to.sink.flow.propagate.safe.text"))!!
|
||||
val propagateAction = myFixture.getAvailableIntention(
|
||||
JvmAnalysisBundle.message("jvm.inspections.source.unsafe.to.sink.flow.propagate.safe.text"))!!
|
||||
myFixture.launchAction(propagateAction)
|
||||
myFixture.checkResultByFile("Property.after.kt")
|
||||
}
|
||||
|
||||
fun testLimits() {
|
||||
prepareCheckFramework()
|
||||
myFixture.addClass("""
|
||||
@SuppressWarnings({"FieldMayBeStatic", "StaticNonFinalField", "RedundantSuppression"})
|
||||
public class Limit2 {
|
||||
|
||||
public final static String fromAnotherFile = "1";
|
||||
public final static String fromAnotherFile2 = fromAnotherFile;
|
||||
public final static String fromAnotherFile3 = fromMethod();
|
||||
public static String fromAnotherFile4 = "1";
|
||||
public final String fromAnotherFile5 = "1";
|
||||
public final String fromAnotherFile6;
|
||||
|
||||
public Limit2() {
|
||||
this.fromAnotherFile6 = "";
|
||||
}
|
||||
|
||||
private static String fromMethod() {
|
||||
return "null";
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
myFixture.testHighlighting("Limits.kt")
|
||||
}
|
||||
|
||||
fun testMethodsAsFields() {
|
||||
prepareCheckFramework()
|
||||
myFixture.addClass("""
|
||||
public class MethodAsFields {
|
||||
private static final String t = "1";
|
||||
|
||||
public String getT() {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
myFixture.testHighlighting("MethodsAsFields.kt")
|
||||
}
|
||||
|
||||
fun testDifferentExpressions() {
|
||||
prepareCheckFramework()
|
||||
myFixture.testHighlighting("DifferentExpressions.kt")
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,6 @@ abstract class SourceToSinkFlowInspectionTestBase : TaintedTestBase() {
|
||||
override val inspection: SourceToSinkFlowInspection = SourceToSinkFlowInspection()
|
||||
|
||||
override fun getProjectDescriptor(): LightProjectDescriptor {
|
||||
return JAVA_8
|
||||
return JAVA_19
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ abstract class TaintedTestBase : JvmInspectionTestBase() {
|
||||
package org.checkerframework.checker.tainting.qual;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Target;
|
||||
@Target({ElementType.LOCAL_VARIABLE, ElementType.FIELD, ElementType.METHOD})
|
||||
@Target({ElementType.LOCAL_VARIABLE, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
|
||||
public @interface Tainted {
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
Reference in New Issue
Block a user