[^ ]+) permission=(?[^ ]+)")
+ | "\(.r)\t\(.p)"' code_scan.json \
+ | while IFS=$'\t' read -r receiver token; do
+ receiver="${receiver#this.}"
+ name=$(resolve_name "$token")
+ [ -z "$name" ] && name="$token"
+ level=$(lookup_level "$name")
+ case "$level" in
+ *signature*|*knownSigner*|*internal*) verdict="OK (strong, untrusted apps cannot hold it)";;
+ "") verdict="REVIEW (permission not declared in this manifest; resolve it elsewhere)";;
+ *) verdict="WEAK -> treat as vulnerable (normal/dangerous can be held by untrusted apps)";;
+ esac
+ echo "$receiver permission=$name protectionLevel=${level:-unknown} -> $verdict"
+ done
+} > evaluation.txt
diff --git a/demos/android/MASVS-PLATFORM/MASTG-DEMO-0x01/evaluation.txt b/demos/android/MASVS-PLATFORM/MASTG-DEMO-0x01/evaluation.txt
new file mode 100644
index 00000000000..bd95f16212e
--- /dev/null
+++ b/demos/android/MASVS-PLATFORM/MASTG-DEMO-0x01/evaluation.txt
@@ -0,0 +1,5 @@
+# Exported, no permission (reported as vulnerable):
+passwordResetReceiver
+
+# Exported, permission-restricted (protection level resolved from the manifest):
+adminCommandReceiver permission=org.owasp.mastestapp.ADMIN_COMMAND_PERMISSION protectionLevel=signature -> OK (strong, untrusted apps cannot hold it)
diff --git a/demos/android/MASVS-PLATFORM/MASTG-DEMO-0x01/manifest_scan.txt b/demos/android/MASVS-PLATFORM/MASTG-DEMO-0x01/manifest_scan.txt
new file mode 100644
index 00000000000..bcae10c10dc
--- /dev/null
+++ b/demos/android/MASVS-PLATFORM/MASTG-DEMO-0x01/manifest_scan.txt
@@ -0,0 +1,32 @@
+
+
+┌────────────────┐
+│ 1 Code Finding │
+└────────────────┘
+
+ AndroidManifest_reversed.xml
+ ❱ rules.mastg-android-receiver-exported-with-permission
+ [MASVS-PLATFORM] Exported broadcast receiver protected by a permission. Verify the permission
+ protection level matches the intended trust boundary:
+ receiver=androidx.profileinstaller.ProfileInstallReceiver
+
+ 72┆
+ 78┆
+ 79┆
+ 80┆
+ 81┆
+ 82┆
+ 83┆
+ 84┆
+ 85┆
+ 86┆
+ 87┆
+ 88┆
+ 89┆
+ 90┆
+
diff --git a/demos/android/MASVS-PLATFORM/MASTG-DEMO-0x01/onreceive_scan.txt b/demos/android/MASVS-PLATFORM/MASTG-DEMO-0x01/onreceive_scan.txt
new file mode 100644
index 00000000000..a2f95abdabd
--- /dev/null
+++ b/demos/android/MASVS-PLATFORM/MASTG-DEMO-0x01/onreceive_scan.txt
@@ -0,0 +1,60 @@
+
+
+┌─────────────────┐
+│ 5 Code Findings │
+└─────────────────┘
+
+ MastgTest_reversed.java
+ ❱ rules.mastg-android-receiver-entrypoint
+ [MASVS-PLATFORM] Broadcast receiver onReceive entry point. Review it and the code it reaches for
+ sensitive functionality that should not be triggerable by an external sender.
+
+ 174┆ @Override // android.content.BroadcastReceiver
+ 175┆ public void onReceive(Context context, Intent intent) {
+ 176┆ Intrinsics.checkNotNullParameter(context, "context");
+ 177┆ Intrinsics.checkNotNullParameter(intent, "intent");
+ 178┆ String newPassword = intent.getStringExtra("newpass");
+ 179┆ if (newPassword == null) {
+ 180┆ return;
+ 181┆ }
+ 182┆ SharedPreferences prefs = context.getSharedPreferences(MastgTest.PREFS, 0);
+ 183┆ String oldPassword = prefs.getString(MastgTest.KEY_PASSWORD_STORE, "");
+ 184┆ Log.d("MASTG-DEMO", "Password changed from " + oldPassword + " to " + newPassword);
+ 185┆ prefs.edit().putString(MastgTest.KEY_PASSWORD_STORE, newPassword).apply();
+ 186┆ }
+
+ ❱ rules.mastg-android-receiver-sensitive-extra-source
+ [MASVS-PLATFORM] Reads an intent extra. When the component is exported and unprotected, this value
+ is attacker-controllable. Trace where it is used.
+
+ 178┆ String newPassword = intent.getStringExtra("newpass");
+
+ ❱ rules.mastg-android-receiver-entrypoint
+ [MASVS-PLATFORM] Broadcast receiver onReceive entry point. Review it and the code it reaches for
+ sensitive functionality that should not be triggerable by an external sender.
+
+ 194┆ @Override // android.content.BroadcastReceiver
+ 195┆ public void onReceive(Context context, Intent intent) {
+ 196┆ Intrinsics.checkNotNullParameter(context, "context");
+ 197┆ Intrinsics.checkNotNullParameter(intent, "intent");
+ 198┆ Log.d("MASTG-DEMO", "Vault updated event received, refreshing UI");
+ 199┆ }
+ ⋮┆----------------------------------------
+ 207┆ @Override // android.content.BroadcastReceiver
+ 208┆ public void onReceive(Context context, Intent intent) {
+ 209┆ Intrinsics.checkNotNullParameter(context, "context");
+ 210┆ Intrinsics.checkNotNullParameter(intent, "intent");
+ 211┆ Log.d("MASTG-DEMO", "AdminCommandReceiver received broadcast: " + intent.getAction());
+ 212┆ if (Intrinsics.areEqual(intent.getStringExtra("command"), "wipe")) {
+ 213┆ context.getSharedPreferences(MastgTest.PREFS,
+ 0).edit().remove(MastgTest.KEY_PASSWORD_STORE).apply();
+ 214┆ Log.d("MASTG-DEMO", "Vault wiped by admin command");
+ 215┆ }
+ 216┆ }
+
+ ❱ rules.mastg-android-receiver-sensitive-extra-source
+ [MASVS-PLATFORM] Reads an intent extra. When the component is exported and unprotected, this value
+ is attacker-controllable. Trace where it is used.
+
+ 212┆ if (Intrinsics.areEqual(intent.getStringExtra("command"), "wipe")) {
+
diff --git a/demos/android/MASVS-PLATFORM/MASTG-DEMO-0x01/permissions_scan.json b/demos/android/MASVS-PLATFORM/MASTG-DEMO-0x01/permissions_scan.json
new file mode 100644
index 00000000000..f7605c495d8
--- /dev/null
+++ b/demos/android/MASVS-PLATFORM/MASTG-DEMO-0x01/permissions_scan.json
@@ -0,0 +1 @@
+{"version":"1.142.1","results":[{"check_id":"rules.mastg-android-declared-permission-level","path":"AndroidManifest_reversed.xml","start":{"line":14,"col":5,"offset":507},"end":{"line":16,"col":46,"offset":633},"extra":{"message":"[MASVS-PLATFORM] Declared permission. permission=org.owasp.mastestapp.ADMIN_COMMAND_PERMISSION level=signature\n","metadata":{"summary":"Reports each custom permission declared in the manifest together with its protection level.","masvs":["MASVS-PLATFORM"]},"severity":"INFO","fingerprint":"requires login","lines":"requires login","validation_state":"NO_VALIDATOR","engine_kind":"OSS"}},{"check_id":"rules.mastg-android-declared-permission-level","path":"AndroidManifest_reversed.xml","start":{"line":17,"col":5,"offset":638},"end":{"line":19,"col":46,"offset":780},"extra":{"message":"[MASVS-PLATFORM] Declared permission. permission=org.owasp.mastestapp.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION level=signature\n","metadata":{"summary":"Reports each custom permission declared in the manifest together with its protection level.","masvs":["MASVS-PLATFORM"]},"severity":"INFO","fingerprint":"requires login","lines":"requires login","validation_state":"NO_VALIDATOR","engine_kind":"OSS"}}],"errors":[],"paths":{"scanned":["AndroidManifest_reversed.xml"]},"time":{"rules":[],"rules_parse_time":0.0017669200897216797,"profiling_times":{"config_time":0.09489679336547852,"core_time":0.3145630359649658,"ignores_time":2.6226043701171875e-05,"total_time":0.4096560478210449},"parsing_time":{"total_time":0.0,"per_file_time":{"mean":0.0,"std_dev":0.0},"very_slow_stats":{"time_ratio":0.0,"count_ratio":0.0},"very_slow_files":[]},"scanning_time":{"total_time":0.006823062896728516,"per_file_time":{"mean":0.006823062896728516,"std_dev":0.0},"very_slow_stats":{"time_ratio":0.0,"count_ratio":0.0},"very_slow_files":[]},"matching_time":{"total_time":0.0,"per_file_and_rule_time":{"mean":0.0,"std_dev":0.0},"very_slow_stats":{"time_ratio":0.0,"count_ratio":0.0},"very_slow_rules_on_files":[]},"tainting_time":{"total_time":0.0,"per_def_and_rule_time":{"mean":0.0,"std_dev":0.0},"very_slow_stats":{"time_ratio":0.0,"count_ratio":0.0},"very_slow_rules_on_defs":[]},"fixpoint_timeouts":[],"prefiltering":{"project_level_time":0.0,"file_level_time":0.0,"rules_with_project_prefilters_ratio":0.0,"rules_with_file_prefilters_ratio":1.0,"rules_selected_ratio":1.0,"rules_matched_ratio":1.0},"targets":[],"total_bytes":0,"max_memory_bytes":77593344},"engine_requested":"OSS","skipped_rules":[]}
diff --git a/demos/android/MASVS-PLATFORM/MASTG-DEMO-0x01/permissions_scan.txt b/demos/android/MASVS-PLATFORM/MASTG-DEMO-0x01/permissions_scan.txt
new file mode 100644
index 00000000000..2acce153690
--- /dev/null
+++ b/demos/android/MASVS-PLATFORM/MASTG-DEMO-0x01/permissions_scan.txt
@@ -0,0 +1,23 @@
+
+
+┌─────────────────┐
+│ 2 Code Findings │
+└─────────────────┘
+
+ AndroidManifest_reversed.xml
+ ❱ rules.mastg-android-declared-permission-level
+ [MASVS-PLATFORM] Declared permission. permission=org.owasp.mastestapp.ADMIN_COMMAND_PERMISSION
+ level=signature
+
+ 14┆
+ ⋮┆----------------------------------------
+ ❱ rules.mastg-android-declared-permission-level
+ [MASVS-PLATFORM] Declared permission.
+ permission=org.owasp.mastestapp.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION level=signature
+
+ 17┆
+
diff --git a/demos/android/MASVS-PLATFORM/MASTG-DEMO-0x01/run.sh b/demos/android/MASVS-PLATFORM/MASTG-DEMO-0x01/run.sh
new file mode 100755
index 00000000000..9c7b25660a4
--- /dev/null
+++ b/demos/android/MASVS-PLATFORM/MASTG-DEMO-0x01/run.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# Context-registered receivers are registered at runtime with Context.registerReceiver and do
+# NOT appear in the AndroidManifest, so manifest enumeration alone misses them.
+
+# Stage 1 (capture, manifest) — enumerate the manifest-declared receivers. This confirms the app
+# declares no receiver of its own in the manifest (only the library ProfileInstallReceiver shows up).
+NO_COLOR=true semgrep -c ../../../../rules/mastg-android-exported-receiver.yml ./AndroidManifest_reversed.xml --text --max-lines-per-finding 0 > manifest_scan.txt
+
+# Stage 1b (capture, permissions) — list the custom permissions declared in the manifest with
+# their protection levels, and flag weak ones (normal/dangerous, or no level) that untrusted apps
+# can hold. evaluate.sh uses this to resolve each receiver's broadcastPermission to its level.
+NO_COLOR=true semgrep -c ../../../../rules/mastg-android-declared-permission-protection-level.yml ./AndroidManifest_reversed.xml --text --max-lines-per-finding 0 > permissions_scan.txt
+NO_COLOR=true semgrep -c ../../../../rules/mastg-android-declared-permission-protection-level.yml ./AndroidManifest_reversed.xml --json > permissions_scan.json 2>/dev/null
+
+# Stage 2 (capture, code) — search the decompiled code for context-registered receivers and
+# classify each registration as exported (RECEIVER_EXPORTED, flag 2), not exported
+# (RECEIVER_NOT_EXPORTED, flag 4), or registered without an explicit flag.
+NO_COLOR=true semgrep -c ../../../../rules/mastg-android-context-registered-receiver.yml ./MastgTest_reversed.java --text --max-lines-per-finding 0 > code_scan.txt
+NO_COLOR=true semgrep -c ../../../../rules/mastg-android-context-registered-receiver.yml ./MastgTest_reversed.java --json > code_scan.json 2>/dev/null
+
+# Stage 3 (inspect code) — locate the onReceive entry points and the attacker-controllable intent
+# extras they read, to review for sensitive functionality.
+NO_COLOR=true semgrep -c ../../../../rules/mastg-android-receiver-entrypoints.yml ./MastgTest_reversed.java --text --max-lines-per-finding 0 > onreceive_scan.txt
diff --git a/rules/mastg-android-activity-entrypoints.yml b/rules/mastg-android-activity-entrypoints.yml
new file mode 100644
index 00000000000..6d135d2f9e0
--- /dev/null
+++ b/rules/mastg-android-activity-entrypoints.yml
@@ -0,0 +1,17 @@
+rules:
+ - id: mastg-android-activity-entrypoint
+ languages: [java]
+ severity: INFO
+ metadata:
+ summary: Locates the lifecycle entry points reachable when an activity is started, so they can be reviewed for sensitive functionality.
+ masvs:
+ - MASVS-PLATFORM
+ message: >
+ [MASVS-PLATFORM] Activity entry point reached when the activity is
+ started. Review it for sensitive functionality that should not be
+ reachable by an external caller.
+ pattern-either:
+ - pattern: void onCreate(...) { ... }
+ - pattern: void onStart(...) { ... }
+ - pattern: void onResume(...) { ... }
+ - pattern: void onNewIntent(...) { ... }
diff --git a/rules/mastg-android-context-registered-receiver.yml b/rules/mastg-android-context-registered-receiver.yml
new file mode 100644
index 00000000000..5a4bfd3e3c8
--- /dev/null
+++ b/rules/mastg-android-context-registered-receiver.yml
@@ -0,0 +1,93 @@
+rules:
+ - id: mastg-android-context-registered-receiver-exported
+ languages: [java]
+ severity: WARNING
+ metadata:
+ summary: Detects a context-registered broadcast receiver that is exported (RECEIVER_EXPORTED) without a broadcastPermission, reachable by other apps.
+ masvs:
+ - MASVS-PLATFORM
+ message: >
+ [MASVS-PLATFORM] Context-registered receiver registered as exported
+ (RECEIVER_EXPORTED) with no broadcastPermission, so any app on the device
+ can deliver broadcasts to it. Inspect its onReceive for sensitive
+ functionality: receiver=$R
+ pattern-either:
+ # Named flag constant, as written in source or preserved by some tools (3/4-arg forms have no permission slot).
+ - patterns:
+ - pattern-either:
+ - pattern: $C.registerReceiver($R, $F, $FLAG)
+ - pattern: ContextCompat.registerReceiver($CTX, $R, $F, $FLAG)
+ - pattern: androidx.core.content.ContextCompat.registerReceiver($CTX, $R, $F, $FLAG)
+ - pattern-regex: \bRECEIVER_EXPORTED\b
+ # Inlined integer flag value (RECEIVER_EXPORTED == 2), as commonly seen in decompiled code.
+ - pattern: $C.registerReceiver($R, $F, 2)
+ - pattern: ContextCompat.registerReceiver($CTX, $R, $F, 2)
+ - pattern: androidx.core.content.ContextCompat.registerReceiver($CTX, $R, $F, 2)
+ # Exported with an explicitly null broadcastPermission is still unprotected.
+ - pattern: $C.registerReceiver($R, $F, null, $S, 2)
+ - pattern: ContextCompat.registerReceiver($CTX, $R, $F, null, $S, 2)
+ - pattern: androidx.core.content.ContextCompat.registerReceiver($CTX, $R, $F, null, $S, 2)
+
+ - id: mastg-android-context-registered-receiver-exported-with-permission
+ languages: [java]
+ severity: INFO
+ metadata:
+ summary: Detects a context-registered broadcast receiver that is exported but restricted with a broadcastPermission.
+ masvs:
+ - MASVS-PLATFORM
+ message: >
+ [MASVS-PLATFORM] Context-registered receiver registered as exported
+ (RECEIVER_EXPORTED) but restricted with a broadcastPermission. Verify the
+ permission protection level matches the intended sender set:
+ receiver=$R permission=$PERM
+ patterns:
+ - pattern-either:
+ - pattern: $C.registerReceiver($R, $F, $PERM, $S, 2)
+ - pattern: ContextCompat.registerReceiver($CTX, $R, $F, $PERM, $S, 2)
+ - pattern: androidx.core.content.ContextCompat.registerReceiver($CTX, $R, $F, $PERM, $S, 2)
+ # An explicitly null broadcastPermission is not protection; it is handled by the rule above.
+ - pattern-not: $C.registerReceiver($R, $F, null, $S, 2)
+ - pattern-not: ContextCompat.registerReceiver($CTX, $R, $F, null, $S, 2)
+ - pattern-not: androidx.core.content.ContextCompat.registerReceiver($CTX, $R, $F, null, $S, 2)
+
+ - id: mastg-android-context-registered-receiver-not-exported
+ languages: [java]
+ severity: INFO
+ metadata:
+ summary: Detects a context-registered broadcast receiver that is not exported (RECEIVER_NOT_EXPORTED).
+ masvs:
+ - MASVS-PLATFORM
+ message: >
+ [MASVS-PLATFORM] Context-registered receiver registered as not exported
+ (RECEIVER_NOT_EXPORTED). Other apps cannot deliver broadcasts to it, so it
+ is not part of the external attack surface: receiver=$R
+ pattern-either:
+ # Named flag constant, as written in source or preserved by some tools.
+ - patterns:
+ - pattern-either:
+ - pattern: $C.registerReceiver($R, ...)
+ - pattern: ContextCompat.registerReceiver($CTX, $R, ...)
+ - pattern: androidx.core.content.ContextCompat.registerReceiver($CTX, $R, ...)
+ - pattern-regex: \bRECEIVER_NOT_EXPORTED\b
+ # Inlined integer flag value (RECEIVER_NOT_EXPORTED == 4), as commonly seen in decompiled code.
+ - pattern: $C.registerReceiver($R, $F, 4)
+ - pattern: ContextCompat.registerReceiver($CTX, $R, $F, 4)
+ - pattern: androidx.core.content.ContextCompat.registerReceiver($CTX, $R, $F, 4)
+ - pattern: $C.registerReceiver($R, $F, $P, $S, 4)
+ - pattern: ContextCompat.registerReceiver($CTX, $R, $F, $P, $S, 4)
+ - pattern: androidx.core.content.ContextCompat.registerReceiver($CTX, $R, $F, $P, $S, 4)
+
+ - id: mastg-android-context-registered-receiver-no-flag
+ languages: [java]
+ severity: WARNING
+ metadata:
+ summary: Detects a two-argument context-registered receiver with no export flag, which is implicitly exported on older target SDKs.
+ masvs:
+ - MASVS-PLATFORM
+ message: >
+ [MASVS-PLATFORM] Context-registered receiver registered without an export
+ flag. On apps targeting below Android 14 (API level 34 makes the flag
+ mandatory for unprotected receivers) this registration is implicitly
+ exported. Confirm the target SDK and treat it as exported unless a
+ broadcastPermission restricts senders: receiver=$R
+ pattern: $C.registerReceiver($R, $F)
diff --git a/rules/mastg-android-declared-permission-protection-level.yml b/rules/mastg-android-declared-permission-protection-level.yml
new file mode 100644
index 00000000000..c42c67a7fab
--- /dev/null
+++ b/rules/mastg-android-declared-permission-protection-level.yml
@@ -0,0 +1,42 @@
+rules:
+ - id: mastg-android-declared-permission-level
+ languages: [xml]
+ severity: INFO
+ metadata:
+ summary: Reports each custom permission declared in the manifest together with its protection level.
+ masvs:
+ - MASVS-PLATFORM
+ message: >
+ [MASVS-PLATFORM] Declared permission. permission=$NAME level=$LEVEL
+ pattern:
+
+ - id: mastg-android-declared-permission-no-level
+ languages: [xml]
+ severity: WARNING
+ metadata:
+ summary: Detects a custom permission declared without a protection level, which defaults to normal (any app can hold it).
+ masvs:
+ - MASVS-PLATFORM
+ message: >
+ [MASVS-PLATFORM] Declared permission with no android:protectionLevel; it
+ defaults to "normal", so any app can be granted it. permission=$NAME level=normal
+ patterns:
+ - pattern:
+ - pattern-not:
+
+ - id: mastg-android-weak-custom-permission
+ languages: [xml]
+ severity: WARNING
+ metadata:
+ summary: Detects a custom permission whose protection level is normal or dangerous, which untrusted apps can obtain.
+ masvs:
+ - MASVS-PLATFORM
+ message: >
+ [MASVS-PLATFORM] Weak custom permission. A "normal" or "dangerous"
+ protection level can be obtained by untrusted apps, so it does not
+ restrict access to trusted-app identity. permission=$NAME level=$LEVEL
+ patterns:
+ - pattern:
+ - metavariable-regex:
+ metavariable: $LEVEL
+ regex: (^|\|)(normal|dangerous)(\||$)
diff --git a/rules/mastg-android-exported-activity.yml b/rules/mastg-android-exported-activity.yml
new file mode 100644
index 00000000000..a7c5d46e21b
--- /dev/null
+++ b/rules/mastg-android-exported-activity.yml
@@ -0,0 +1,28 @@
+rules:
+ - id: mastg-android-activity-exported-without-permission
+ languages: [xml]
+ severity: WARNING
+ metadata:
+ summary: Detects an exported activity that does not enforce any caller permission.
+ masvs:
+ - MASVS-PLATFORM
+ message: >
+ [MASVS-PLATFORM] Exported activity without android:permission, reachable
+ by any app on the device. Inspect its code for sensitive functionality:
+ activity=$NAME
+ patterns:
+ - pattern:
+ - pattern-not:
+
+ - id: mastg-android-activity-exported-with-permission
+ languages: [xml]
+ severity: INFO
+ metadata:
+ summary: Detects an exported activity that enforces a caller permission.
+ masvs:
+ - MASVS-PLATFORM
+ message: >
+ [MASVS-PLATFORM] Exported activity protected by a permission. Verify the
+ permission protection level matches the intended trust boundary:
+ activity=$NAME
+ pattern:
diff --git a/rules/mastg-android-exported-receiver.yml b/rules/mastg-android-exported-receiver.yml
new file mode 100644
index 00000000000..8cddba8852d
--- /dev/null
+++ b/rules/mastg-android-exported-receiver.yml
@@ -0,0 +1,28 @@
+rules:
+ - id: mastg-android-receiver-exported-without-permission
+ languages: [xml]
+ severity: WARNING
+ metadata:
+ summary: Detects an exported, manifest-declared broadcast receiver that does not enforce any sender permission.
+ masvs:
+ - MASVS-PLATFORM
+ message: >
+ [MASVS-PLATFORM] Exported broadcast receiver without android:permission,
+ to which any app on the device can send broadcasts. Inspect its onReceive
+ for sensitive functionality: receiver=$NAME
+ patterns:
+ - pattern:
+ - pattern-not:
+
+ - id: mastg-android-receiver-exported-with-permission
+ languages: [xml]
+ severity: INFO
+ metadata:
+ summary: Detects an exported, manifest-declared broadcast receiver that enforces a sender permission.
+ masvs:
+ - MASVS-PLATFORM
+ message: >
+ [MASVS-PLATFORM] Exported broadcast receiver protected by a permission.
+ Verify the permission protection level matches the intended trust
+ boundary: receiver=$NAME
+ pattern:
diff --git a/rules/mastg-android-exported-service.yml b/rules/mastg-android-exported-service.yml
new file mode 100644
index 00000000000..dfc21a021dd
--- /dev/null
+++ b/rules/mastg-android-exported-service.yml
@@ -0,0 +1,28 @@
+rules:
+ - id: mastg-android-service-exported-without-permission
+ languages: [xml]
+ severity: WARNING
+ metadata:
+ summary: Detects an exported service that does not enforce any caller permission.
+ masvs:
+ - MASVS-PLATFORM
+ message: >
+ [MASVS-PLATFORM] Exported service without android:permission, which any
+ app on the device can start or bind to. Inspect its code for sensitive
+ functionality: service=$NAME
+ patterns:
+ - pattern:
+ - pattern-not:
+
+ - id: mastg-android-service-exported-with-permission
+ languages: [xml]
+ severity: INFO
+ metadata:
+ summary: Detects an exported service that enforces a caller permission.
+ masvs:
+ - MASVS-PLATFORM
+ message: >
+ [MASVS-PLATFORM] Exported service protected by a permission. Verify the
+ permission protection level matches the intended trust boundary:
+ service=$NAME
+ pattern:
diff --git a/rules/mastg-android-receiver-entrypoints.yml b/rules/mastg-android-receiver-entrypoints.yml
new file mode 100644
index 00000000000..46e5be34549
--- /dev/null
+++ b/rules/mastg-android-receiver-entrypoints.yml
@@ -0,0 +1,32 @@
+rules:
+ - id: mastg-android-receiver-entrypoint
+ languages: [java]
+ severity: INFO
+ metadata:
+ summary: Locates the onReceive entry point of a broadcast receiver, so it can be reviewed for sensitive functionality.
+ masvs:
+ - MASVS-PLATFORM
+ message: >
+ [MASVS-PLATFORM] Broadcast receiver onReceive entry point. Review it and
+ the code it reaches for sensitive functionality that should not be
+ triggerable by an external sender.
+ pattern: void onReceive(...) { ... }
+
+ - id: mastg-android-receiver-sensitive-extra-source
+ languages: [java]
+ severity: INFO
+ metadata:
+ summary: Detects reading attacker-controllable intent extras inside receiver/component code.
+ masvs:
+ - MASVS-PLATFORM
+ message: >
+ [MASVS-PLATFORM] Reads an intent extra. When the component is exported and
+ unprotected, this value is attacker-controllable. Trace where it is used.
+ pattern-either:
+ - pattern: $I.getStringExtra(...)
+ - pattern: $I.getIntExtra(...)
+ - pattern: $I.getBooleanExtra(...)
+ - pattern: $I.getByteArrayExtra(...)
+ - pattern: $I.getSerializableExtra(...)
+ - pattern: $I.getParcelableExtra(...)
+ - pattern: $I.getExtras(...)
diff --git a/rules/mastg-android-service-entrypoints.yml b/rules/mastg-android-service-entrypoints.yml
new file mode 100644
index 00000000000..07e670f1484
--- /dev/null
+++ b/rules/mastg-android-service-entrypoints.yml
@@ -0,0 +1,35 @@
+rules:
+ - id: mastg-android-service-entrypoint
+ languages: [java]
+ severity: INFO
+ metadata:
+ summary: Locates the entry points reachable when a service is started or bound, so they can be reviewed for sensitive functionality.
+ masvs:
+ - MASVS-PLATFORM
+ message: >
+ [MASVS-PLATFORM] Service entry point reached when the service is started
+ or bound. Review it for sensitive functionality that should not be
+ reachable by an external caller.
+ pattern-either:
+ - pattern: $T onStartCommand(...) { ... }
+ - pattern: $T onBind(...) { ... }
+ - pattern: $T onRebind(...) { ... }
+ - pattern: $T onHandleIntent(...) { ... }
+
+ - id: mastg-android-component-caller-permission-check
+ languages: [java]
+ severity: INFO
+ metadata:
+ summary: Detects a runtime caller-permission check, which can restrict which apps reach the component.
+ masvs:
+ - MASVS-PLATFORM
+ message: >
+ [MASVS-PLATFORM] Runtime caller-permission check found. Confirm it gates
+ the sensitive functionality and that the enforced permission has an
+ appropriate protection level. Its absence means there is no runtime
+ restriction on the caller.
+ pattern-either:
+ - pattern: $C.checkCallingPermission(...)
+ - pattern: $C.enforceCallingPermission(...)
+ - pattern: $C.checkCallingOrSelfPermission(...)
+ - pattern: $C.enforceCallingOrSelfPermission(...)
diff --git a/tests-beta/android/MASVS-PLATFORM/MASTG-TEST-0364.md b/tests-beta/android/MASVS-PLATFORM/MASTG-TEST-0364.md
index a6ed8a6aa46..26b7947ff24 100644
--- a/tests-beta/android/MASVS-PLATFORM/MASTG-TEST-0364.md
+++ b/tests-beta/android/MASVS-PLATFORM/MASTG-TEST-0364.md
@@ -3,7 +3,7 @@ platform: android
title: Exported And Unprotected Activities That Expose Sensitive Functionality
id: MASTG-TEST-0364
type: [static, config, code, manual]
-weakness: MASWE-0x01
+weakness: MASWE-0119
best-practices: [MASTG-BEST-0052]
profiles: [L1, L2]
knowledge: [MASTG-KNOW-0132, MASTG-KNOW-0017, MASTG-KNOW-0020]
@@ -24,22 +24,52 @@ This test checks whether the app exposes sensitive functionality through exporte
## Observation
-The output should contain a list of exported activities and the relevant parts of their implementation.
+The output should contain all activities from the app. For each activity, record:
+
+1. Activity name or class.
+2. Accepted actions, categories, data schemes, hosts, paths, or intent filters.
+3. Export state by recording `android:exported`.
+4. Required caller permission, if any, by recording `android:permission` and its protection level.
+5. Relevant entry points or flows reachable when the activity is started, for example `onCreate`, `onStart`, `onResume`, `onNewIntent`, or any flow reached from those methods.
## Evaluation
-The test case fails if any exported activity is not protected by an appropriate `android:permission` that restricts which apps can start it and exposes or performs sensitive functionality, for example by displaying sensitive data, performing a security-relevant action, or allowing a caller to bypass authentication.
+The test fails only if all of the following are true:
+
+1. The activity is exported. For example, an activity with `android:exported="true"`.
+2. The activity does not enforce strong caller protection.
+3. The activity exposes or performs sensitive functionality.
**Further Validation Required:**
-Inspect each exported activity using @MASTG-TECH-0023 to determine whether it exposes sensitive functionality:
+Use the following decision flow:
+
+```mermaid
+flowchart TD
+ A[Activity] --> B{Exported}
+ B -->|No| C[Pass]
+ B -->|Yes| D{Strong caller protection}
+ D -->|Yes| C
+ D -->|No| E{Sensitive functionality}
+ E -->|No| C
+ E -->|Yes| F[Fail]
+```
+
+Strong caller protection means that the activity enforces a permission or equivalent access control appropriate for the intended caller set. A `signature` permission is usually appropriate when only apps signed by the same developer or organization should be able to start the activity.
+
+The activity does not enforce strong caller protection when any of the following applies:
+
+1. It has no required caller permission.
+2. It uses a `normal` permission.
+3. It uses a `dangerous` permission where trusted-app identity is required.
+4. It uses a custom permission that is not declared with `` by the target app or another trusted package.
+5. It uses a custom permission that is declared incorrectly, for example with the wrong name or the wrong protection level.
+
+A `dangerous` permission may be acceptable only when the intended trust boundary is user-granted access rather than trusted-app identity. For example, this may be acceptable when the activity is intentionally available to any app that the user allowed to hold a specific runtime permission. It is not appropriate when the activity should only accept requests from a specific trusted app, vendor app, companion app, or same-signer app.
-- Determine whether the activity displays or returns sensitive data (for example, account details, messages, or stored secrets).
-- Determine whether the activity performs a security-relevant action (for example, changing settings or credentials).
-- Determine whether starting the activity directly bypasses an authentication step, such as a login or PIN screen, that the app relies on elsewhere.
+A custom permission is only strong protection if it resolves to a trusted `` declaration with an appropriate protection level, usually `signature`. The trusted declaration may be in the target app, a trusted companion app, a trusted SDK package, the platform, or an OEM package.
-Then determine whether external access to the activity is appropriately restricted for the functionality it exposes and the app's intended trust boundary:
+Inspect each exported activity using @MASTG-TECH-0023 to determine whether `onCreate`, `onStart`, `onResume`, `onNewIntent`, or any flow reached from those methods exposes or performs sensitive functionality.
-- Determine whether the activity has a legitimate reason to be started by third-party apps. If it doesn't, it shouldn't be exported.
-- If external access is required, determine whether the activity is protected by an appropriate `android:permission` or an equivalent access control. Appropriate means the control matches the sensitivity of the activity and the set of apps that should be allowed to start it.
-- Verify that the permission is effective for that trust boundary, for example by using a `signature` protection level or another control that is not broadly grantable to untrusted apps.
+!!! info
+ `MainActivity` is a special case because it declares `MAIN` and `LAUNCHER`. Android expects launcher activities to be exported so the launcher can start the app, so `android:exported="true"` is not itself a vulnerability. However, the activity is still externally reachable and must be reviewed. If it displays sensitive data, performs sensitive actions, or processes attacker controlled intent data without enforcing authentication or authorization inside the activity, it can still be vulnerable. A custom signature permission is generally not appropriate for the real launcher activity because ordinary launcher apps are not signed with the app's certificate.
\ No newline at end of file
diff --git a/tests-beta/android/MASVS-PLATFORM/MASTG-TEST-0365.md b/tests-beta/android/MASVS-PLATFORM/MASTG-TEST-0365.md
index 5b945c9cbbf..f4cdc25aad0 100644
--- a/tests-beta/android/MASVS-PLATFORM/MASTG-TEST-0365.md
+++ b/tests-beta/android/MASVS-PLATFORM/MASTG-TEST-0365.md
@@ -24,22 +24,40 @@ This test checks whether the app exposes sensitive functionality through exporte
## Observation
-The output should contain a list of exported services and the relevant parts of their implementation, including the interface they expose and any permission checks they perform.
+The output should contain all services from the app. For each service, record:
+
+1. Service name or class.
+2. Whether it is started, bound, or both.
+3. Accepted actions or intent filters.
+4. Export state by recording `android:exported`.
+5. Required caller permission, if any, by recording `android:permission` and its protection level.
+6. Exposed bound-service interface, if any, for example Binder, Messenger, AIDL, or another bound-service API.
+7. Relevant runtime caller checks, if any, for example `checkCallingPermission`, `enforceCallingPermission`, `checkCallingOrSelfPermission`, or `enforceCallingOrSelfPermission`.
+8. Every entry point or flow reachable when the service is started or bound, for example `onStartCommand`, `onBind`, `onRebind`, `onHandleIntent`, or any exposed bound-service interface method.
## Evaluation
-The test case fails if any exported service is not protected by an appropriate `android:permission` that restricts which apps can start or bind to it and exposes or performs sensitive functionality, for example by returning sensitive data, performing a security-relevant action, or allowing a caller to invoke a bound-service interface without authorization.
+The test fails only if all of the following are true:
+
+1. The service is exported. For example, a service with `android:exported="true"`.
+2. The service does not enforce strong caller protection.
+3. The service exposes or performs sensitive functionality.
**Further Validation Required:**
-Inspect each exported service using @MASTG-TECH-0023 to determine whether it exposes sensitive functionality:
+Use the following decision flow:
-- Determine whether the service returns sensitive data or performs a security-relevant action (for example, changing a password or PIN) in response to a request.
-- Determine whether the service exposes a started-service or bound-service interface that lets callers trigger sensitive operations or access sensitive data.
+```mermaid
+flowchart TD
+ A[Service] --> B{Exported}
+ B -->|No| C[Pass]
+ B -->|Yes| D{Strong caller protection}
+ D -->|Yes| C
+ D -->|No| E{Sensitive functionality}
+ E -->|No| C
+ E -->|Yes| F[Fail]
+```
-Then determine whether external access to the service is appropriately restricted for the functionality it exposes and the app's intended trust boundary:
+Strong caller protection means that the service enforces a permission or equivalent access control appropriate for the intended caller set. Use the same protection-level criteria described in @MASTG-TEST-0364.
-- Determine whether the service has a legitimate reason to accept start or bind requests from third-party apps. If it doesn't, it shouldn't be exported.
-- If external access is required, determine whether the service is protected by an appropriate `android:permission` or an equivalent access control. Appropriate means the control matches the sensitivity of the service operation and the set of apps that should be allowed to start or bind to it.
-- Verify that the permission is effective for that trust boundary, for example by using a `signature` protection level or another control that is not broadly grantable to untrusted apps.
-- Determine whether the service verifies the caller's permission at runtime (for example, with `checkCallingPermission` or `enforceCallingPermission`) before processing sensitive requests.
+Inspect each exported service using @MASTG-TECH-0023 to determine whether `onStartCommand`, `onBind`, `onRebind`, `onHandleIntent`, or any exposed bound-service interface reaches sensitive functionality.
diff --git a/tests-beta/android/MASVS-PLATFORM/MASTG-TEST-0366.md b/tests-beta/android/MASVS-PLATFORM/MASTG-TEST-0366.md
index 3af5177e6f1..2f486c94063 100644
--- a/tests-beta/android/MASVS-PLATFORM/MASTG-TEST-0366.md
+++ b/tests-beta/android/MASVS-PLATFORM/MASTG-TEST-0366.md
@@ -11,7 +11,11 @@ knowledge: [MASTG-KNOW-0134, MASTG-KNOW-0017, MASTG-KNOW-0020]
## Overview
-If an exported receiver does not define [`android:permission`](https://developer.android.com/guide/topics/manifest/receiver-element#prmsn) with a proper protection level and performs or grants access to sensitive functionality, another third-party app outside the intended trust boundary can send a broadcast to it and invoke that functionality. See @MASTG-KNOW-0134 for details on broadcast receivers, @MASTG-KNOW-0017 for permissions and protection levels, and @MASTG-KNOW-0020 for the IPC model of Android.
+If a broadcast receiver is exported, not protected against untrusted senders, and performs or grants access to sensitive functionality, another third-party app outside the intended trust boundary can send a broadcast to it and invoke that functionality from their `onReceive` method.
+
+For manifest-declared receivers, relevant manifest attributes include [`android:exported`](https://developer.android.com/guide/topics/manifest/receiver-element#exported) and [`android:permission`](https://developer.android.com/guide/topics/manifest/receiver-element#prmsn). For context-registered receivers, relevant APIs and arguments include the `Context.registerReceiver()` and `ContextCompat.registerReceiver()` overloads that set `broadcastPermission` and `flags` (especially the `RECEIVER_EXPORTED` and `RECEIVER_NOT_EXPORTED` flags).
+
+See @MASTG-KNOW-0x03 for the full list of relevant overloads and more general details on broadcast receivers.
This test checks whether the app exposes sensitive functionality through exported and unprotected broadcast receivers.
@@ -19,27 +23,47 @@ This test checks whether the app exposes sensitive functionality through exporte
1. Use @MASTG-TECH-0013 to reverse engineer the app.
2. Use @MASTG-TECH-0117 to obtain the AndroidManifest.xml.
-3. Use @MASTG-TECH-0162 to list the exported broadcast receivers and their associated `android:permission`, including context-registered receivers found in the code.
-4. Use @MASTG-TECH-0014 to inspect the `onReceive` implementation of each exported receiver.
+3. Use @MASTG-TECH-0x03 to list manifest-declared broadcast receivers, their export status, intent filters, and associated `android:permission`.
+4. Use @MASTG-TECH-0014 to look for the relevant APIs.
## Observation
-The output should contain a list of exported broadcast receivers and the relevant parts of their `onReceive` implementation, including how they use data from the received intent and any permission they require.
+The output should contain all broadcast receivers from the app. For each receiver, record:
+
+1. Receiver name or class.
+2. Whether it is manifest-declared or context-registered.
+3. Accepted actions or intent filters.
+4. Export state:
+ - For manifest-declared receivers, record `android:exported`.
+ - For context-registered receivers, record `RECEIVER_EXPORTED`, `RECEIVER_NOT_EXPORTED`, or the absence of an explicit flag.
+5. Required sender permission, if any:
+ - For manifest-declared receivers, record `android:permission` and its protection level.
+ - For context-registered receivers, record `broadcastPermission` and its protection level.
+6. Its `onReceive` implementation.
## Evaluation
-The test case fails if any exported broadcast receiver is not protected by an appropriate `android:permission` that restricts which apps can send broadcasts to it and exposes or performs sensitive functionality, for example by performing a security-relevant action or disclosing sensitive data when it receives a broadcast.
+The test fails only if all of the following are true:
+
+1. The receiver is exported. For example, a manifest-declared receiver with `android:exported="true"` or a context-registered receiver with `RECEIVER_EXPORTED`.
+2. The receiver does not enforce strong sender protection.
+3. The receiver exposes or performs sensitive functionality.
**Further Validation Required:**
-Inspect each exported broadcast receiver using @MASTG-TECH-0023 to determine whether it exposes sensitive functionality:
+Use the following decision flow:
-- Determine whether `onReceive` performs a security-relevant action or discloses sensitive data based on the received intent (for example, reading extras and using them to send a message or change state).
-- Determine whether the receiver validates the data it reads from the intent before acting on it.
+```mermaid
+flowchart TD
+ A[Broadcast receiver] --> B{Exported}
+ B -->|No| C[Pass]
+ B -->|Yes| D{Strong sender protection}
+ D -->|Yes| C
+ D -->|No| E{Sensitive functionality}
+ E -->|No| C
+ E -->|Yes| F[Fail]
+```
-Then determine whether external access to the receiver is appropriately restricted for the functionality it exposes and the app's intended trust boundary:
+Strong sender protection means that the receiver enforces a permission or equivalent access control appropriate for the intended sender set. Use the same protection-level criteria described in @MASTG-TEST-0364.
-- Determine whether the receiver has a legitimate reason to accept broadcasts from third-party apps. If it doesn't, it shouldn't be exported.
-- If external access is required, determine whether the receiver is protected by an appropriate `android:permission` or an equivalent access control. Appropriate means the control matches the sensitivity of the receiver action and the set of apps that should be allowed to send broadcasts to it.
-- Verify that the permission is effective for that trust boundary, for example by using a `signature` protection level or another control that is not broadly grantable to untrusted apps.
-- Determine whether context-registered receivers are registered with `RECEIVER_NOT_EXPORTED` when they don't need to receive broadcasts from other apps.
+Inspect each exported broadcast receiver using @MASTG-TECH-0023 to determine whether `onReceive` or any code reached from it exposes or performs sensitive functionality.