Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

querydb: make android query work for kotlin & java #2009

Merged
merged 4 commits into from
Nov 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1610,8 +1610,18 @@ trait KtPsiToAst {
argIdx: Option[Int],
argName: Option[String] = None
)(implicit typeInfoProvider: TypeInfoProvider): Ast = {
val typeFullName = registerType(typeInfoProvider.typeFullName(expr, TypeConstants.any))
val name = expr.getIdentifier.getText
val fallBackTypeName = scope.lookupVariable(expr.getIdentifier.getText) match {
ursachec marked this conversation as resolved.
Show resolved Hide resolved
case Some(n: NewLocal) => n.typeFullName
case Some(n: NewMethodParameterIn) => n.typeFullName
case None => Defines.UnresolvedNamespace
}
val typeFromProvider = typeInfoProvider.typeFullName(expr, fallBackTypeName)
val typeFullName =
if (typeFromProvider == Defines.UnresolvedNamespace && fallBackTypeName != Defines.UnresolvedNamespace)
registerType(fallBackTypeName)
else
registerType(typeFromProvider)
val name = expr.getIdentifier.getText
val node =
withArgumentName(withArgumentIndex(identifierNode(name, typeFullName, line(expr), column(expr)), argIdx), argName)
astWithRefEdgeMaybe(name, node)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,11 +193,23 @@ class DefaultTypeInfoProvider(environment: KotlinCoreEnvironment) extends TypeIn

def propertyType(expr: KtProperty, defaultValue: String): String = {
val mapForEntity = bindingsForEntity(bindingContext, expr)
Option(mapForEntity.get(BindingContext.VARIABLE.getKey))
.map(_.getType)
.map(TypeRenderer.render(_))
.filter(isValidRender)
.getOrElse(defaultValue)
val render =
Option(mapForEntity.get(BindingContext.VARIABLE.getKey))
.map(_.getType)
.map(TypeRenderer.render(_))
.filter(isValidRender)

render match {
case Some(value) if value == Defines.UnresolvedNamespace =>
Option(expr.getTypeReference)
.map { typeRef =>
typeFromImports(typeRef.getText, expr.getContainingKtFile).getOrElse(typeRef.getText)
}
.getOrElse(defaultValue)
case Some(aValue) => aValue
case None => defaultValue
}

}

def typeFullName(expr: KtClassOrObject, defaultValue: String): String = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,40 @@ class IdentifierTests extends KotlinCode2CpgFixture(withOssDataflow = false) {
cpg.identifier("args").columnNumber.l.head shouldBe 18
}
}

"CPG for code with various missing types, but some information available in the imports" should {
val cpg = code("""package io.vrooom.vulnerableapp
|
|import android.os.Bundle
|import android.support.v7.app.AppCompatActivity
|import android.webkit.WebView
|import android.content.IntentFilter
|
|class MainActivity : AppCompatActivity() {
| override fun onCreate(savedInstanceState: Bundle?) {
| super.onCreate(savedInstanceState)
| setContentView(R.layout.activity_main)
|
| val filter: IntentFilter = IntentFilter()
| filter.addAction(packageName + "io.vrooom.intent.action.WRITE_FILE")
| val receiver = WriteFileBroadcastReceiver()
| registerReceiver(receiver, filter)
| }
|}
|""".stripMargin)

"contain a LOCAL node with the TYPE_FULL_NAME taken from the imports" in {
cpg.identifier("filter").typeFullName.l.head shouldBe "android.content.IntentFilter"
}

"contain an IDENTIFIER node with the TYPE_FULL_NAME taken from the LOCAL node" in {
cpg.call
.code("registerReceiver.*")
.argument
.codeExact("filter")
.isIdentifier
.typeFullName
.head shouldBe "android.content.IntentFilter"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ object ArbitraryFileWrites extends QueryBundle {
def exposedBroadcastReceiverData =
exposedBroadcastReceivers.method.nameExact("onReceive").parameter.index(2)
def fileWriteCalls =
cpg.call.methodFullNameExact("java.io.FileOutputStream.write:void(byte[])")
cpg.call.nameExact("write").where(_.argument.isIdentifier.typeFullNameExact("java.io.FileOutputStream"))
fileWriteCalls.where(_.argument.reachableBy(exposedBroadcastReceiverData))
}),
tags = List(QueryTags.android),
Expand Down Expand Up @@ -134,6 +134,98 @@ object ArbitraryFileWrites extends QueryBundle {
|""".stripMargin,
"AndroidManifest.xml"
)
),
List(
CodeSnippet(
"""|package io.vrooom.vulnerableapp
|
|import android.content.BroadcastReceiver
|import android.content.Context
|import android.content.Intent
|import android.util.Log
|import java.io.FileOutputStream
|import java.io.IOException
|import java.nio.charset.Charset
|
|class WriteFileBroadcastReceiver : BroadcastReceiver() {
| override fun onReceive(context: Context?, intent: Intent?) {
| val fileNameFromBroadcast = intent!!.getStringExtra("filename")
| val contentFromBroadcast = intent!!.getStringExtra("content")
| try {
| val filePath = context!!.filesDir.toString() + fileNameFromBroadcast
| val outputStream: FileOutputStream = FileOutputStream(filePath)
| val strToBytes = contentFromBroadcast!!.toByteArray(Charset.defaultCharset())
| outputStream.write(strToBytes)
| outputStream.close()
| Log.d("WriteFileBroadcastReceiver", "filePath: " + filePath)
| Log.d("WriteFileBroadcastReceiver", "content: " + contentFromBroadcast)
| } catch (ex: IOException) {
| Log.d("exception", ex.getLocalizedMessage())
| }
| }
|}
|""".stripMargin,
"WriteFileBroadcastReceiver.kt"
),
CodeSnippet(
"""|package io.vrooom.vulnerableapp
|
|import android.os.Bundle
|import android.support.v7.app.AppCompatActivity
|import android.webkit.WebView
|import android.content.IntentFilter
|
|class MainActivity : AppCompatActivity() {
| override fun onCreate(savedInstanceState: Bundle?) {
| super.onCreate(savedInstanceState)
| setContentView(R.layout.activity_main)
|
| val filter: IntentFilter = IntentFilter()
| filter.addAction(packageName + "io.vrooom.intent.action.WRITE_FILE")
| val receiver = WriteFileBroadcastReceiver()
| registerReceiver(receiver, filter)
| }
|}
|""".stripMargin,
"MainActivity.kt"
),
CodeSnippet(
"""|<?xml version="1.0" encoding="utf-8"?>
|<manifest xmlns:android="http://schemas.android.com/apk/res/android"
| xmlns:tools="http://schemas.android.com/tools">
| <uses-permission android:name="android.permission.INTERNET" />
| <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
| <application
| android:allowBackup="true"
| android:dataExtractionRules="@xml/data_extraction_rules"
| android:fullBackupContent="@xml/backup_rules"
| android:usesCleartextTraffic="true"
| android:icon="@mipmap/ic_launcher"
| android:label="@string/app_name"
| android:roundIcon="@mipmap/ic_launcher_round"
| android:supportsRtl="true"
| android:theme="@style/Theme.Vulnerableapp"
| android:requestLegacyExternalStorage="true"
| tools:targetApi="31">
| <activity
| android:name=".MainActivity"
| android:exported="true">
| <intent-filter>
| <action android:name="android.intent.action.MAIN" />
| <category android:name="android.intent.category.LAUNCHER" />
| </intent-filter>
| </activity>
| <receiver android:name=".WriteFileBroadcastReceiver" android:exported="true">
| <intent-filter>
| <action android:name="io.vrooom.intent.action.WRITE_FILE" />
| </intent-filter>
| </receiver>
| </application>
|</manifest>
|""".stripMargin,
"AndroidManifest.xml"
)
)
),
negative = List(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.joern.scanners.android

import io.joern.suites.{JavaQueryTestSuite}

class ArbitraryFileWritesJavaTests extends JavaQueryTestSuite(ArbitraryFileWrites) {
"the `broadcastToFileWrite` query" when {
"should match on all multi-file positive examples" in {
val q = queryBundle.broadcastToFileWrite()
q.multiFileCodeExamples.positive
.filter(_.nonEmpty)
.filter { snippets =>
snippets.filterNot(_.filename.endsWith(".xml")).forall(_.filename.endsWith(".java"))
}
.foreach { snippets =>
val cpg = cpgForSnippets(snippets)
findMatchingCalls(cpg, q).size shouldBe 1
}
}
}

"the `broadcastToFileWrite` query" when {
"should not match on all multi-file negative examples" in {
val q = queryBundle.broadcastToFileWrite()
q.multiFileCodeExamples.negative
.filter(_.nonEmpty)
.filter { snippets =>
snippets.filterNot(_.filename.endsWith(".xml")).forall(_.filename.endsWith(".java"))
}
.foreach { snippets =>
val cpg = cpgForSnippets(snippets)
findMatchingCalls(cpg, q).size shouldBe 0
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.joern.scanners.android

import io.joern.suites.{KotlinQueryTestSuite}

class ArbitraryFileWritesKotlinTests extends KotlinQueryTestSuite(ArbitraryFileWrites) {
"the `broadcastToFileWrite` query" when {
"should match on all multi-file positive examples" in {
val q = queryBundle.broadcastToFileWrite()
q.multiFileCodeExamples.positive
.filter(_.nonEmpty)
.filter { snippets =>
snippets.filterNot(_.filename.endsWith(".xml")).forall(_.filename.endsWith(".kt"))
}
.foreach { snippets =>
val cpg = cpgForSnippets(snippets)
findMatchingCalls(cpg, q).size shouldBe 1
}
}
}

"the `broadcastToFileWrite` query" when {
"should not match on all multi-file negative examples" in {
val q = queryBundle.broadcastToFileWrite()
q.multiFileCodeExamples.negative
.filter(_.nonEmpty)
.filter { snippets =>
snippets.filterNot(_.filename.endsWith(".xml")).forall(_.filename.endsWith(".kt"))
}
.foreach { snippets =>
val cpg = cpgForSnippets(snippets)
findMatchingCalls(cpg, q).size shouldBe 0
}
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ class NodeTypeStarters(cpg: Cpg) {
def registerReceiver: Traversal[Call] =
cpg.call
.nameExact("registerReceiver")
.typeFullNameExact("void")
.where(_.argument(2).isIdentifier.typeFullNameExact("android.content.IntentFilter"))

def registeredBroadcastReceivers =
Expand Down