11/** Definitions for the keyboard cache query */
22
33import java
4+ import semmle.code.java.dataflow.DataFlow
45import semmle.code.java.security.SensitiveActions
56import semmle.code.xml.AndroidManifest
67
@@ -45,6 +46,62 @@ class AndroidEditableXmlElement extends AndroidLayoutXmlElement {
4546 string getInputType ( ) { result = this .getAttribute ( "inputType" ) .( AndroidXmlAttribute ) .getValue ( ) }
4647}
4748
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+
48105/** Gets a regex indicating that an input field may contain sensitive data. */
49106private string getInputSensitiveInfoRegex ( ) {
50107 result =
@@ -54,7 +111,7 @@ private string getInputSensitiveInfoRegex() {
54111 ]
55112}
56113
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. */
58115bindingset [ ty]
59116private predicate inputTypeCached ( string ty ) {
60117 ty .matches ( "%text%" ) and
@@ -64,5 +121,13 @@ private predicate inputTypeCached(string ty) {
64121/** Gets an input field whose contents may be sensitive and may be stored in the keyboard cache. */
65122AndroidEditableXmlElement getASensitiveCachedInput ( ) {
66123 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+ )
68133}
0 commit comments