Skip to content

Commit 3c060ae

Browse files
authored
Merge branch 'master' into port-MASTG-TEST-0033
2 parents 90ac7e0 + a79bbc4 commit 3c060ae

File tree

18 files changed

+396
-8
lines changed

18 files changed

+396
-8
lines changed

.github/workflows/build-android-demos.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ jobs:
155155
cp -f "$demo/MastgTest.kt" mas-app-android/app/src/main/java/org/owasp/mastestapp/MastgTest.kt 2>/dev/null \
156156
&& echo "Copied MastgTest.kt for $demo" \
157157
|| echo "No MastgTest.kt found for $demo"
158+
cp -f "$demo/MainActivity.kt" mas-app-android/app/src/main/java/org/owasp/mastestapp/MainActivity.kt 2>/dev/null \
159+
&& echo "Copied MainActivity.kt for $demo" \
160+
|| echo "No MainActivity.kt found for $demo"
158161
cp -f "$demo/MastgTestWebView.kt" mas-app-android/app/src/main/java/org/owasp/mastestapp/MastgTestWebView.kt 2>/dev/null \
159162
&& echo "Copied MastgTestWebView.kt for $demo" \
160163
|| echo "No MastgTestWebView.kt found for $demo"
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
platform: android
3+
title: Sensitive User Data Sent to Firebase Analytics with Frida
4+
id: MASTG-DEMO-0081
5+
code: [kotlin]
6+
test: MASTG-TEST-0319
7+
---
8+
9+
## Sample
10+
11+
This sample collects the following [sensitive user data](https://support.google.com/googleplay/android-developer/answer/10787469?hl=en#types&zippy=%2Cdata-types) and sends it to Firebase Analytics using the `logEvent` method:
12+
13+
- User ID (**Data type:** User IDs, **Category:** Personal info)
14+
- Blood type (**Data type:** Health info, **Category:** Health and fitness)
15+
16+
For the sake of this demo, we pretend that the app is published on Google Play and that the data types collected are not disclosed in the [Data safety section](https://support.google.com/googleplay/android-developer/answer/10787469?hl=en#types&zippy=%2Cdata-types).
17+
18+
{{ MainActivity.kt # MastgTest.kt # build.gradle.kts.libs }}
19+
20+
## Steps
21+
22+
1. Install the app on a device (@MASTG-TECH-0005)
23+
2. Make sure you have @MASTG-TOOL-0001 installed on your machine and the frida-server running on the device
24+
3. Run `run.sh` to spawn the app with Frida
25+
4. Select a blood type from the dropdown
26+
5. Click the **Start** button
27+
6. Stop the script by pressing `Ctrl+C` and/or `q` to quit the Frida CLI
28+
29+
{{ hooks.js # run.sh }}
30+
31+
## Observation
32+
33+
The output shows all instances of `logEvent` calls to the Firebase Analytics SDK found at runtime, along with the parameters sent. A backtrace is also provided to help identify the location in the code.
34+
35+
{{ output.json }}
36+
37+
## Evaluation
38+
39+
This test **fails** because sensitive data (`blood_type` parameter) is being sent to Firebase Analytics via the `logEvent` method for a particular user (`user_id` parameter) and this data collection is not disclosed in the Data safety section on Google Play (as we indicated in the sample description).
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
package org.owasp.mastestapp
2+
3+
import android.os.Bundle
4+
import androidx.activity.ComponentActivity
5+
import androidx.activity.compose.setContent
6+
import androidx.activity.enableEdgeToEdge
7+
import androidx.compose.foundation.layout.Column
8+
import androidx.compose.foundation.layout.padding
9+
import androidx.compose.material3.Button
10+
import androidx.compose.material3.DropdownMenu
11+
import androidx.compose.material3.DropdownMenuItem
12+
import androidx.compose.material3.Text
13+
import androidx.compose.runtime.Composable
14+
import androidx.compose.runtime.getValue
15+
import androidx.compose.runtime.mutableStateOf
16+
import androidx.compose.runtime.remember
17+
import androidx.compose.runtime.setValue
18+
import androidx.compose.ui.Modifier
19+
import androidx.compose.ui.graphics.Color
20+
import androidx.compose.ui.platform.LocalContext
21+
import androidx.compose.ui.platform.testTag
22+
import androidx.compose.ui.text.AnnotatedString
23+
import androidx.compose.ui.text.SpanStyle
24+
import androidx.compose.ui.text.buildAnnotatedString
25+
import androidx.compose.ui.text.font.FontFamily
26+
import androidx.compose.ui.text.withStyle
27+
import androidx.compose.ui.tooling.preview.Preview
28+
import androidx.compose.ui.unit.dp
29+
import androidx.compose.ui.unit.sp
30+
import kotlinx.serialization.json.Json
31+
import kotlinx.serialization.json.JsonArray
32+
import kotlinx.serialization.json.decodeFromJsonElement
33+
34+
const val MASTG_TEXT_TAG = "mastgTestText"
35+
36+
class MainActivity : ComponentActivity() {
37+
override fun onCreate(savedInstanceState: Bundle?) {
38+
super.onCreate(savedInstanceState)
39+
enableEdgeToEdge()
40+
setContent {
41+
MainScreen()
42+
}
43+
}
44+
}
45+
46+
fun UpdateDisplayString(
47+
defaultMessage: String,
48+
result: String
49+
): AnnotatedString {
50+
return buildAnnotatedString {
51+
append(defaultMessage)
52+
try {
53+
val jsonArrayFromString = Json.parseToJsonElement(result) as JsonArray
54+
val demoResults = jsonArrayFromString.map { Json.decodeFromJsonElement<DemoResult>(it) }
55+
56+
for (demoResult in demoResults) {
57+
when (demoResult.status) {
58+
Status.PASS -> {
59+
withStyle(style = SpanStyle(color = Color.Green)) {
60+
append("MASTG-DEMO-${demoResult.demoId} demonstrated a successful test:\n${demoResult.message}\n\n")
61+
}
62+
}
63+
64+
Status.FAIL -> {
65+
withStyle(style = SpanStyle(color = Color(0xFFFF9800))) {
66+
append("MASTG-DEMO-${demoResult.demoId} demonstrated a failed test:\n${demoResult.message}\n\n")
67+
}
68+
}
69+
70+
Status.ERROR -> {
71+
withStyle(style = SpanStyle(color = Color.Red)) {
72+
append("MASTG-DEMO-${demoResult.demoId} failed:\n${demoResult.message}\n\n")
73+
}
74+
}
75+
}
76+
}
77+
} catch (_: Exception) {
78+
// not a valid set of DemoResult, so print the result without any parsing
79+
append(result)
80+
}
81+
}
82+
83+
}
84+
85+
@Preview
86+
@Composable
87+
fun MainScreen() {
88+
val defaultMessage = "Click \"Start\" to send the data.\n\n"
89+
var displayString by remember { mutableStateOf(buildAnnotatedString { append(defaultMessage) }) }
90+
var selectedBloodType by remember { mutableStateOf("") }
91+
val context = LocalContext.current
92+
val mastgTestClass = MastgTest(context)
93+
// By default run the test in a separate thread, this ensures that network tests such as those using SSLSocket work properly.
94+
// However, some tests which interact with UI elements need to run on the main thread.
95+
// You can set shouldRunInMainThread = true in MastgTest.kt for those tests.
96+
val runInMainThread = MastgTest::class.members
97+
.find { it.name == "shouldRunInMainThread" }
98+
?.call(mastgTestClass) as? Boolean ?: false
99+
100+
BaseScreen(
101+
onStartClick = {
102+
if (runInMainThread) {
103+
val result = mastgTestClass.mastgTest(selectedBloodType)
104+
displayString = UpdateDisplayString(defaultMessage, result)
105+
} else {
106+
Thread {
107+
val result = mastgTestClass.mastgTest(selectedBloodType)
108+
android.os.Handler(android.os.Looper.getMainLooper()).post {
109+
displayString = UpdateDisplayString(defaultMessage, result)
110+
}
111+
}.start()
112+
}
113+
}
114+
) {
115+
Column(modifier = Modifier.padding(16.dp)) {
116+
// Normal visible selection UI: list of radio buttons for blood types
117+
val bloodTypes = listOf("A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-")
118+
119+
var expanded by remember { mutableStateOf(false) }
120+
121+
Button(onClick = { expanded = !expanded }) {
122+
Text("Select Blood Type")
123+
}
124+
DropdownMenu(
125+
expanded = expanded,
126+
onDismissRequest = { expanded = false }
127+
) {
128+
bloodTypes.forEach {
129+
DropdownMenuItem(
130+
text = { Text(it) },
131+
onClick = {
132+
selectedBloodType = it
133+
expanded = false
134+
}
135+
)
136+
}
137+
}
138+
139+
if (selectedBloodType.isNotEmpty()) {
140+
Text(
141+
modifier = Modifier.padding(vertical = 16.dp),
142+
color = Color.White,
143+
text = "Selected Blood Type: $selectedBloodType"
144+
)
145+
}
146+
147+
Text(
148+
modifier = Modifier
149+
.padding(top = 8.dp)
150+
.testTag(MASTG_TEXT_TAG),
151+
text = displayString,
152+
color = Color.White,
153+
fontSize = 16.sp,
154+
fontFamily = FontFamily.Monospace
155+
)
156+
}
157+
}
158+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package org.owasp.mastestapp
2+
3+
import android.content.Context
4+
import com.google.firebase.analytics.FirebaseAnalytics
5+
import com.google.firebase.analytics.logEvent
6+
import kotlin.random.Random
7+
8+
class MastgTest(context: Context) {
9+
10+
val analytics = FirebaseAnalytics.getInstance(context)
11+
12+
// Random arbitrary number for the sake of the demo
13+
val userId: String = (1..8).map { Random.nextInt(0, 10) }.joinToString("")
14+
15+
fun mastgTest(bloodType: String): String {
16+
analytics.logEvent("user_blood_type") {
17+
param("user_id", userId)
18+
param("blood_type", bloodType)
19+
}
20+
21+
return """
22+
'user_blood_type' event was sent to Firebase Analytics.
23+
24+
User id: $userId
25+
Blood type: $bloodType
26+
""".trimIndent()
27+
}
28+
}
94.4 KB
Loading
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Running on Pixel_4_API_34_Manually_Created
2+
> Flow flow
3+
Take screenshot before... COMPLETED
4+
Tap on "Select Blood Type"... COMPLETED
5+
Tap on "A+"... COMPLETED
6+
Tap on "Start"... COMPLETED
7+
Take screenshot after... COMPLETED
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
FLOW="flow.yaml"
5+
6+
# Start Frida and redirect stdout and stderr to file
7+
./run.sh 2>&1 &
8+
9+
FRIDA_PID=$!
10+
11+
# Run Maestro (https://docs.maestro.dev/getting-started/installing-maestro)
12+
maestro test "$FLOW" > auto.log 2>&1
13+
MAESTRO_EXIT=$?
14+
15+
# Stop Frida when Maestro completes
16+
kill "$FRIDA_PID" 2>/dev/null || true
17+
18+
exit "$MAESTRO_EXIT"
60.7 KB
Loading
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
implementation("com.google.firebase:firebase-analytics:23.0.0")
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
appId: org.owasp.mastestapp
2+
---
3+
#- launchApp
4+
- takeScreenshot: before
5+
- tapOn: "Select Blood Type"
6+
- tapOn: "A+"
7+
- tapOn: "Start"
8+
- takeScreenshot: after

0 commit comments

Comments
 (0)