blob: 59fc2a14ba3f6b98c7e2583fa439cb9dc674f4a6 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.lint.checks
import com.android.tools.lint.checks.SecureRandomDetector.Companion.JAVA_SECURITY_SECURE_RANDOM
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.ConstantEvaluator
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Implementation
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.intellij.psi.CommonClassNames.JAVA_LANG_STRING
import com.intellij.psi.PsiMethod
import org.jetbrains.uast.UCallExpression
/**
* Flags code that is asking for a provider that is no longer available
* in more recent versions of the platform.
*/
class DeletedProviderDetector : Detector(), SourceCodeScanner {
override fun getApplicableMethodNames(): List<String> {
return listOf(GET_INSTANCE)
}
override fun visitMethodCall(
context: JavaContext,
node: UCallExpression,
method: PsiMethod
) {
val qualifiedName = method.containingClass?.qualifiedName ?: return
if (qualifiedName != JAVAX_CRYPTO_CIPHER && qualifiedName != JAVA_SECURITY_SECURE_RANDOM) {
return
}
// These are the matching methods for {Cipher,SecureRandom}.getInstance(...) :
// public static SecureRandom getInstance(String algorithm)
// public static SecureRandom getInstance(String algorithm, String provider)
// public static SecureRandom getInstance(String algorithm, Provider provider)
// public static final Cipher getInstance(String transformation)
// public static final Cipher getInstance(String transformation, String provider)
// public static final Cipher getInstance(String transformation, Provider provider)
// We care about the ones passing in a string provider.
val arguments = node.valueArguments
if (arguments.size != 2) {
return
}
val expression = arguments[1]
val expressionType = expression.getExpressionType()
if (expressionType != null && expressionType.canonicalText != JAVA_LANG_STRING) {
return
}
val value = ConstantEvaluator.evaluate(context, expression)
if (value == "Crypto") {
context.report(
ISSUE, expression, context.getLocation(expression),
"The Crypto provider has been deleted " +
"in Android P (and was deprecated in Android N), so the code will crash"
)
}
}
companion object {
@JvmField
val ISSUE = Issue.create(
id = "DeletedProvider",
briefDescription = "Using Deleted Provider",
explanation = """
The `Crypto` provider has been completely removed in Android P (and was \
deprecated in an earlier release). This means that the code will throw a \
`NoSuchProviderException` and the app will crash. Even if the code catches \
that exception at a higher level, this is not secure and should not be \
used.
""",
moreInfo = "https://android-developers.googleblog.com/2018/03/cryptography-changes-in-android-p.html",
category = Category.SECURITY,
priority = 9,
severity = Severity.ERROR,
androidSpecific = true,
implementation = Implementation(
DeletedProviderDetector::class.java,
Scope.JAVA_FILE_SCOPE
)
)
private const val JAVAX_CRYPTO_CIPHER = "javax.crypto.Cipher"
private const val GET_INSTANCE = "getInstance"
}
}