1
1
/** Definitions for the keyboard cache query */
2
2
3
3
import java
4
+ import semmle.code.java.dataflow.DataFlow
4
5
import semmle.code.java.security.SensitiveActions
5
6
import semmle.code.xml.AndroidManifest
6
7
@@ -45,6 +46,62 @@ class AndroidEditableXmlElement extends AndroidLayoutXmlElement {
45
46
string getInputType ( ) { result = this .getAttribute ( "inputType" ) .( AndroidXmlAttribute ) .getValue ( ) }
46
47
}
47
48
49
+ /** Gets a use of the view that has the given id. */
50
+ private Expr getAUseOfId ( string id ) {
51
+ exists ( string name , MethodAccess findView , NestedClass r_id , Field id_field |
52
+ id = "@+id/" + name and
53
+ findView
54
+ .getMethod ( )
55
+ .hasQualifiedName ( "android.view" , "View" , [ "findViewById" , "requireViewById" ] ) and
56
+ r_id .getEnclosingType ( ) .hasName ( "R" ) and
57
+ r_id .hasName ( "id" ) and
58
+ id_field .getDeclaringType ( ) = r_id and
59
+ id_field .hasName ( name )
60
+ |
61
+ DataFlow:: localExprFlow ( id_field .getAnAccess ( ) , findView .getArgument ( 0 ) ) and
62
+ result = findView
63
+ )
64
+ }
65
+
66
+ /** Gets the argument of a use of `setInputType` called on the view with the given id. */
67
+ private Expr setInputTypeForId ( string id ) {
68
+ exists ( MethodAccess setInputType |
69
+ setInputType .getMethod ( ) .hasQualifiedName ( "android.widget" , "TextView" , "setInputType" ) and
70
+ DataFlow:: localExprFlow ( getAUseOfId ( id ) , setInputType .getQualifier ( ) ) and
71
+ result = setInputType .getArgument ( 0 )
72
+ )
73
+ }
74
+
75
+ /** Holds if the given field is a constant flag indicating that an input with this type will not be cached. */
76
+ private predicate inputTypeFieldNotCached ( Field f ) {
77
+ f .getDeclaringType ( ) .hasQualifiedName ( "android.text" , "InputType" ) and
78
+ (
79
+ not f .getName ( ) .matches ( "%TEXT%" )
80
+ or
81
+ f .getName ( ) .matches ( "%PASSWORD%" )
82
+ or
83
+ f .getName ( ) = "TYPE_TEXT_FLAG_NO_SUGGESTIONS"
84
+ )
85
+ }
86
+
87
+ /** Configuration that finds uses of `setInputType` that for non cached fields. */
88
+ private class GoodInputTypeConf extends DataFlow:: Configuration {
89
+ GoodInputTypeConf ( ) { this = "GoodInputTypeConf" }
90
+
91
+ override predicate isSource ( DataFlow:: Node node ) {
92
+ inputTypeFieldNotCached ( node .asExpr ( ) .( FieldAccess ) .getField ( ) )
93
+ }
94
+
95
+ override predicate isSink ( DataFlow:: Node node ) { node .asExpr ( ) = setInputTypeForId ( _) }
96
+
97
+ override predicate isAdditionalFlowStep ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
98
+ exists ( OrBitwiseExpr bitOr |
99
+ node1 .asExpr ( ) = bitOr .getAChildExpr ( ) and
100
+ node2 .asExpr ( ) = bitOr
101
+ )
102
+ }
103
+ }
104
+
48
105
/** Gets a regex indicating that an input field may contain sensitive data. */
49
106
private string getInputSensitiveInfoRegex ( ) {
50
107
result =
@@ -54,7 +111,7 @@ private string getInputSensitiveInfoRegex() {
54
111
]
55
112
}
56
113
57
- /** Holds if input using the given input type may be stored in the keyboard cache. */
114
+ /** Holds if input using the given input type (as written in XML) may be stored in the keyboard cache. */
58
115
bindingset [ ty]
59
116
private predicate inputTypeCached ( string ty ) {
60
117
ty .matches ( "%text%" ) and
@@ -64,5 +121,13 @@ private predicate inputTypeCached(string ty) {
64
121
/** Gets an input field whose contents may be sensitive and may be stored in the keyboard cache. */
65
122
AndroidEditableXmlElement getASensitiveCachedInput ( ) {
66
123
result .getId ( ) .regexpMatch ( getInputSensitiveInfoRegex ( ) ) and
67
- inputTypeCached ( result .getInputType ( ) )
124
+ (
125
+ inputTypeCached ( result .getInputType ( ) )
126
+ or
127
+ not exists ( result .getInputType ( ) ) and
128
+ not exists ( GoodInputTypeConf conf , DataFlow:: Node src , DataFlow:: Node sink |
129
+ conf .hasFlow ( src , sink ) and
130
+ sink .asExpr ( ) = setInputTypeForId ( result .getId ( ) )
131
+ )
132
+ )
68
133
}
0 commit comments