1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.android.lint.aidl
18 
19 import com.android.tools.lint.detector.api.Category
20 import com.android.tools.lint.detector.api.Implementation
21 import com.android.tools.lint.detector.api.Incident
22 import com.android.tools.lint.detector.api.Issue
23 import com.android.tools.lint.detector.api.JavaContext
24 import com.android.tools.lint.detector.api.Scope
25 import com.android.tools.lint.detector.api.Severity
26 import org.jetbrains.uast.UBlockExpression
27 import org.jetbrains.uast.UMethod
28 
29 /**
30  * Looks for methods implementing generated AIDL interface stubs
31  * that can have simple permission checks migrated to
32  * @EnforcePermission annotations
33  */
34 @Suppress("UnstableApiUsage")
35 class SimpleManualPermissionEnforcementDetector : AidlImplementationDetector() {
36     override fun visitAidlMethod(
37             context: JavaContext,
38             node: UMethod,
39             interfaceName: String,
40             body: UBlockExpression
41     ) {
42         val enforcePermissionFix = EnforcePermissionFix.fromBlockExpression(context, body) ?: return
43         val lintFix = enforcePermissionFix.toLintFix(context, node)
44         val message =
45                 "$interfaceName permission check ${
46                     if (enforcePermissionFix.errorLevel) "should" else "can"
47                 } be converted to @EnforcePermission annotation"
48 
49         val incident = Incident(
50                 ISSUE_SIMPLE_MANUAL_PERMISSION_ENFORCEMENT,
51                 enforcePermissionFix.manualCheckLocations.last(),
52                 message,
53                 lintFix
54         )
55 
56         // TODO(b/265014041): turn on errors once all code that would cause one is fixed
57         // if (enforcePermissionFix.errorLevel) {
58         //     incident.overrideSeverity(Severity.ERROR)
59         // }
60 
61         context.report(incident)
62     }
63 
64     companion object {
65 
66         private val EXPLANATION = """
67             Whenever possible, method implementations of AIDL interfaces should use the @EnforcePermission
68             annotation to declare the permissions to be enforced.  The verification code is then
69             generated by the AIDL compiler, which also takes care of annotating the generated java
70             code.
71 
72             This reduces the risk of bugs around these permission checks (that often become vulnerabilities).
73             It also enables easier auditing and review.
74 
75             Please migrate to an @EnforcePermission annotation. (See: go/aidl-enforce-howto)
76         """.trimIndent()
77 
78         @JvmField
79         val ISSUE_SIMPLE_MANUAL_PERMISSION_ENFORCEMENT = Issue.create(
80                 id = "SimpleManualPermissionEnforcement",
81                 briefDescription = "Manual permission check can be @EnforcePermission annotation",
82                 explanation = EXPLANATION,
83                 category = Category.SECURITY,
84                 priority = 5,
85                 severity = Severity.WARNING,
86                 implementation = Implementation(
87                         SimpleManualPermissionEnforcementDetector::class.java,
88                         Scope.JAVA_FILE_SCOPE
89                 ),
90         )
91     }
92 }
93