mirror of
https://github.com/github/codeql.git
synced 2026-04-29 02:35:15 +02:00
Implement checks for calls that may safely mask information
This commit is contained in:
69
java/ql/lib/semmle/code/java/frameworks/android/Layout.qll
Normal file
69
java/ql/lib/semmle/code/java/frameworks/android/Layout.qll
Normal file
@@ -0,0 +1,69 @@
|
||||
/** Provides classes and predicates for working with Android layouts and UI elements. */
|
||||
|
||||
import java
|
||||
import semmle.code.xml.AndroidManifest
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
|
||||
/** An Android Layout XML file. */
|
||||
class AndroidLayoutXmlFile extends XmlFile {
|
||||
AndroidLayoutXmlFile() { this.getRelativePath().matches("%/res/layout/%.xml") }
|
||||
}
|
||||
|
||||
/** A component declared in an Android layout file. */
|
||||
class AndroidLayoutXmlElement extends XmlElement {
|
||||
AndroidXmlAttribute id;
|
||||
|
||||
AndroidLayoutXmlElement() {
|
||||
this.getFile() instanceof AndroidLayoutXmlFile and
|
||||
id = this.getAttribute("id")
|
||||
}
|
||||
|
||||
/** Gets the ID of this component. */
|
||||
string getId() { result = id.getValue() }
|
||||
|
||||
/** Gets the class of this component. */
|
||||
Class getClass() {
|
||||
this.getName() = "view" and
|
||||
this.getAttribute("class").getValue() = result.getQualifiedName()
|
||||
or
|
||||
this.getName() = result.getQualifiedName()
|
||||
or
|
||||
result.hasQualifiedName(["android.widget", "android.view"], this.getName())
|
||||
}
|
||||
}
|
||||
|
||||
/** An XML element that represents an editable text field. */
|
||||
class AndroidEditableXmlElement extends AndroidLayoutXmlElement {
|
||||
AndroidEditableXmlElement() {
|
||||
this.getClass().getASourceSupertype*().hasQualifiedName("android.widget", "EditText")
|
||||
}
|
||||
|
||||
/** Gets the input type of this field, if any. */
|
||||
string getInputType() { result = this.getAttribute("inputType").(AndroidXmlAttribute).getValue() }
|
||||
}
|
||||
|
||||
/** A `findViewById` or `requireViewById` method on `Activity` or `View`. */
|
||||
private class FindViewMethod extends Method {
|
||||
FindViewMethod() {
|
||||
this.hasQualifiedName("android.view", "View", ["findViewById", "requireViewById"])
|
||||
or
|
||||
exists(Method m |
|
||||
m.hasQualifiedName("android.app", "Activity", ["findViewById", "requireViewById"]) and
|
||||
this = m.getAnOverride*()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets a use of the view that has the given id. (i.e. from a call to a method like `findViewById`) */
|
||||
MethodCall getAUseOfViewWithId(string id) {
|
||||
exists(string name, NestedClass r_id, Field id_field |
|
||||
id = "@+id/" + name and
|
||||
result.getMethod() instanceof FindViewMethod and
|
||||
r_id.getEnclosingType().hasName("R") and
|
||||
r_id.hasName("id") and
|
||||
id_field.getDeclaringType() = r_id and
|
||||
id_field.hasName(name)
|
||||
|
|
||||
DataFlow::localExprFlow(id_field.getAnAccess(), result.getArgument(0))
|
||||
)
|
||||
}
|
||||
@@ -3,71 +3,7 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.security.SensitiveActions
|
||||
import semmle.code.xml.AndroidManifest
|
||||
|
||||
/** An Android Layout XML file. */
|
||||
private class AndroidLayoutXmlFile extends XmlFile {
|
||||
AndroidLayoutXmlFile() { this.getRelativePath().matches("%/res/layout/%.xml") }
|
||||
}
|
||||
|
||||
/** A component declared in an Android layout file. */
|
||||
class AndroidLayoutXmlElement extends XmlElement {
|
||||
AndroidXmlAttribute id;
|
||||
|
||||
AndroidLayoutXmlElement() {
|
||||
this.getFile() instanceof AndroidLayoutXmlFile and
|
||||
id = this.getAttribute("id")
|
||||
}
|
||||
|
||||
/** Gets the ID of this component. */
|
||||
string getId() { result = id.getValue() }
|
||||
|
||||
/** Gets the class of this component. */
|
||||
Class getClass() {
|
||||
this.getName() = "view" and
|
||||
this.getAttribute("class").getValue() = result.getQualifiedName()
|
||||
or
|
||||
this.getName() = result.getQualifiedName()
|
||||
or
|
||||
result.hasQualifiedName(["android.widget", "android.view"], this.getName())
|
||||
}
|
||||
}
|
||||
|
||||
/** An XML element that represents an editable text field. */
|
||||
class AndroidEditableXmlElement extends AndroidLayoutXmlElement {
|
||||
AndroidEditableXmlElement() {
|
||||
this.getClass().getASourceSupertype*().hasQualifiedName("android.widget", "EditText")
|
||||
}
|
||||
|
||||
/** Gets the input type of this field, if any. */
|
||||
string getInputType() { result = this.getAttribute("inputType").(AndroidXmlAttribute).getValue() }
|
||||
}
|
||||
|
||||
/** A `findViewById` or `requireViewById` method on `Activity` or `View`. */
|
||||
private class FindViewMethod extends Method {
|
||||
FindViewMethod() {
|
||||
this.hasQualifiedName("android.view", "View", ["findViewById", "requireViewById"])
|
||||
or
|
||||
exists(Method m |
|
||||
m.hasQualifiedName("android.app", "Activity", ["findViewById", "requireViewById"]) and
|
||||
this = m.getAnOverride*()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets a use of the view that has the given id. */
|
||||
private MethodCall getAUseOfViewWithId(string id) {
|
||||
exists(string name, NestedClass r_id, Field id_field |
|
||||
id = "@+id/" + name and
|
||||
result.getMethod() instanceof FindViewMethod and
|
||||
r_id.getEnclosingType().hasName("R") and
|
||||
r_id.hasName("id") and
|
||||
id_field.getDeclaringType() = r_id and
|
||||
id_field.hasName(name)
|
||||
|
|
||||
DataFlow::localExprFlow(id_field.getAnAccess(), result.getArgument(0))
|
||||
)
|
||||
}
|
||||
import semmle.code.java.frameworks.android.Layout
|
||||
|
||||
/** Gets the argument of a use of `setInputType` called on the view with the given id. */
|
||||
private Argument setInputTypeForId(string id) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import java
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.security.SensitiveActions
|
||||
private import semmle.code.java.frameworks.android.Layout
|
||||
|
||||
/** A configuration for tracking sensitive information to system notifications. */
|
||||
private module NotificationTrackingConfig implements DataFlow::ConfigSig {
|
||||
@@ -42,16 +43,38 @@ private class SetTextCall extends MethodCall {
|
||||
Expr getStringArgument() { result = this.getArgument(0) }
|
||||
}
|
||||
|
||||
/** A call to a method indicating that the contents of a UI element are safely masked. */
|
||||
private class MaskCall extends MethodCall {
|
||||
MaskCall() {
|
||||
this.getMethod().hasQualifiedName("android.widget", "TextView", "setInputType")
|
||||
or
|
||||
this.getMethod().hasQualifiedName("android.widget", "view", "setVisibility")
|
||||
}
|
||||
}
|
||||
|
||||
/** A configuration for tracking sensitive information to text fields. */
|
||||
private module TextFieldTrackingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SensitiveExpr }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink.asExpr() = any(SetTextCall s).getStringArgument() }
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(SetTextCall call |
|
||||
sink.asExpr() = call.getStringArgument() and
|
||||
not isMasked(call)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if the qualifier of `call` is also called with a method that may mask the information displayed. */
|
||||
private predicate isMasked(SetTextCall call) {
|
||||
exists(string id |
|
||||
DataFlow::localExprFlow(getAUseOfViewWithId(id), call.getQualifier()) and
|
||||
DataFlow::localExprFlow(getAUseOfViewWithId(id), any(MaskCall mcall).getQualifier())
|
||||
)
|
||||
}
|
||||
|
||||
/** Taint tracking flow for sensitive data flowing to text fields. */
|
||||
module TextFieldTracking = TaintTracking::Global<NotificationTrackingConfig>;
|
||||
|
||||
Reference in New Issue
Block a user