mirror of
https://github.com/github/codeql.git
synced 2026-05-02 12:15:17 +02:00
Added query for Cleartext Storage in Android Database
This commit is contained in:
@@ -132,6 +132,8 @@ private module Frameworks {
|
||||
private import semmle.code.java.frameworks.Hibernate
|
||||
private import semmle.code.java.frameworks.jOOQ
|
||||
private import semmle.code.java.frameworks.spring.SpringHttp
|
||||
private import semmle.code.java.frameworks.android.ContentProviders
|
||||
private import semmle.code.java.frameworks.android.Widget
|
||||
}
|
||||
|
||||
private predicate sourceModelCsv(string row) {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Provides classes and predicates for working with Content Providers.
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/** The class `android.content.ContentValues`. */
|
||||
class ContentValues extends Class {
|
||||
ContentValues() { this.hasQualifiedName("android.content", "ContentValues") }
|
||||
}
|
||||
|
||||
private class SummaryModels extends SummaryModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
"android.content;ContentValues;false;put;;;Argument[0];MapKey of Argument[-1];value",
|
||||
"android.content;ContentValues;false;put;;;Argument[1];MapValue of Argument[-1];value",
|
||||
"android.content;ContentValues;false;putAll;;;MapKey of Argument[0];MapKey of Argument[-1];value",
|
||||
"android.content;ContentValues;false;putAll;;;MapValue of Argument[0];MapValue of Argument[-1];value"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,14 @@ class TypeDatabaseUtils extends Class {
|
||||
TypeDatabaseUtils() { hasQualifiedName("android.database", "DatabaseUtils") }
|
||||
}
|
||||
|
||||
class TypeSQLiteOpenHelper extends Class {
|
||||
TypeSQLiteOpenHelper() { this.hasQualifiedName("android.database.sqlite", "SQLiteOpenHelper") }
|
||||
}
|
||||
|
||||
class TypeSQLiteStatement extends Class {
|
||||
TypeSQLiteStatement() { this.hasQualifiedName("android.database.sqlite", "SQLiteStatement") }
|
||||
}
|
||||
|
||||
private class SQLiteSinkCsv extends SinkModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
|
||||
21
java/ql/lib/semmle/code/java/frameworks/android/Widget.qll
Normal file
21
java/ql/lib/semmle/code/java/frameworks/android/Widget.qll
Normal file
@@ -0,0 +1,21 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.ExternalFlow
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
private class AndroidWidgetSourceModels extends SourceModelCsv {
|
||||
override predicate row(string row) {
|
||||
row = ["android.widget;EditText;true;getText;;;ReturnValue;android-widget"]
|
||||
}
|
||||
}
|
||||
|
||||
private class DefaultAndroidWidgetSources extends RemoteFlowSource {
|
||||
DefaultAndroidWidgetSources() { sourceNode(this, "android-widget") }
|
||||
|
||||
override string getSourceType() { result = "Android widget source" }
|
||||
}
|
||||
|
||||
private class AndroidWidgetSummaryModels extends SummaryModelCsv {
|
||||
override predicate row(string row) {
|
||||
row = ["android.widget;EditText;true;getText;;;Argument[-1];ReturnValue;taint"]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/** Provides classes and predicates to reason about cleartext storage in Android databases. */
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.frameworks.android.ContentProviders
|
||||
import semmle.code.java.frameworks.android.Intent
|
||||
import semmle.code.java.frameworks.android.SQLite
|
||||
import semmle.code.java.security.CleartextStorageQuery
|
||||
|
||||
private class LocalDatabaseCleartextStorageSink extends CleartextStorageSink {
|
||||
LocalDatabaseCleartextStorageSink() { localDatabaseInput(_, this.asExpr()) }
|
||||
}
|
||||
|
||||
private class LocalDatabaseCleartextStorageStep extends CleartextStorageAdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
// EditText.getText() return type is parsed as `Object`, so we need to
|
||||
// add a taint step for `Object.toString` to model `editText.getText().toString()`
|
||||
exists(MethodAccess ma, Method m |
|
||||
ma.getMethod() = m and
|
||||
m.getDeclaringType() instanceof TypeObject and
|
||||
m.hasName("toString")
|
||||
|
|
||||
n1.asExpr() = ma.getQualifier() and n2.asExpr() = ma
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** The creation of an object that can be used to store data in a local database. */
|
||||
class LocalDatabaseOpenMethodAccess extends Storable, Call {
|
||||
LocalDatabaseOpenMethodAccess() {
|
||||
exists(Method m | this.(MethodAccess).getMethod() = m |
|
||||
m.getDeclaringType().getASupertype*() instanceof TypeSQLiteOpenHelper and
|
||||
m.hasName("getWritableDatabase")
|
||||
or
|
||||
m.getDeclaringType() instanceof TypeSQLiteDatabase and
|
||||
m.hasName(["create", "open%Database", "compileStatement"])
|
||||
or
|
||||
m.getDeclaringType().getASupertype*() instanceof TypeContext and
|
||||
m.hasName("openOrCreateDatabase")
|
||||
)
|
||||
or
|
||||
this.(ClassInstanceExpr).getConstructedType() instanceof ContentValues
|
||||
}
|
||||
|
||||
override Expr getAnInput() {
|
||||
exists(LocalDatabaseFlowConfig config, DataFlow::Node database |
|
||||
localDatabaseInput(database, result) and
|
||||
config.hasFlow(DataFlow::exprNode(this), database)
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getAStore() {
|
||||
exists(LocalDatabaseFlowConfig config, DataFlow::Node database |
|
||||
localDatabaseStore(database, result) and
|
||||
config.hasFlow(DataFlow::exprNode(this), database)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A method that is both a database input and a database store. */
|
||||
private class LocalDatabaseInputStoreMethod extends Method {
|
||||
LocalDatabaseInputStoreMethod() {
|
||||
this.getDeclaringType() instanceof TypeSQLiteDatabase and
|
||||
this.getName().matches("exec%SQL")
|
||||
}
|
||||
}
|
||||
|
||||
private predicate localDatabaseInput(DataFlow::Node database, Argument input) {
|
||||
exists(Method m | input.getCall().(MethodAccess).getMethod() = m |
|
||||
m instanceof LocalDatabaseInputStoreMethod and
|
||||
database.asExpr() = input.getCall().getQualifier()
|
||||
or
|
||||
m.getDeclaringType() instanceof TypeSQLiteDatabase and
|
||||
m.hasName("compileStatement") and
|
||||
database.asExpr() = input.getCall()
|
||||
or
|
||||
m.getDeclaringType() instanceof ContentValues and
|
||||
m.hasName("put") and
|
||||
input.getPosition() = 1 and
|
||||
database.asExpr() = input.getCall().getQualifier()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate localDatabaseStore(DataFlow::Node database, MethodAccess store) {
|
||||
exists(Method m | store.getMethod() = m |
|
||||
m instanceof LocalDatabaseInputStoreMethod and
|
||||
database.asExpr() = store.getQualifier()
|
||||
or
|
||||
m.getDeclaringType() instanceof TypeSQLiteDatabase and
|
||||
m.getName().matches(["insert%", "replace%", "update%"]) and
|
||||
database.asExpr() = store.getAnArgument() and
|
||||
database.getType() instanceof ContentValues
|
||||
or
|
||||
m.getDeclaringType() instanceof TypeSQLiteStatement and
|
||||
m.hasName(["executeInsert", "executeUpdateDelete"]) and
|
||||
database.asExpr() = store.getQualifier()
|
||||
)
|
||||
}
|
||||
|
||||
private class LocalDatabaseFlowConfig extends DataFlow::Configuration {
|
||||
LocalDatabaseFlowConfig() { this = "LocalDatabaseFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr() instanceof LocalDatabaseOpenMethodAccess
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
localDatabaseInput(sink, _) or
|
||||
localDatabaseStore(sink, _)
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
exists(Field f |
|
||||
f.getType() instanceof TypeSQLiteDatabase and
|
||||
f.getAnAssignedValue() = n1.asExpr() and
|
||||
f = n2.asExpr().(FieldRead).getField()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -14,13 +14,19 @@
|
||||
import java
|
||||
|
||||
private string suspicious() {
|
||||
result = ["%password%", "%passwd%", "%account%", "%accnt%", "%trusted%"]
|
||||
result =
|
||||
[
|
||||
"%password%", "%passwd%", "pwd", "%account%", "%accnt%", "%trusted%", "%refresh%token%",
|
||||
"%secret%token"
|
||||
]
|
||||
}
|
||||
|
||||
private string nonSuspicious() {
|
||||
result = "%hashed%" or
|
||||
result = "%encrypted%" or
|
||||
result = "%crypt%"
|
||||
result = "%crypt%" or
|
||||
result = "%create table%" or
|
||||
result = "%drop table%"
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user