Skip to content

VNX-360: Android receiver for a system broadcast trusts the intent without checking its action

Reports BroadcastReceiver.onReceive methods (Java, Kotlin) that act on a system broadcast such as BOOT_COMPLETED or SMS_RECEIVED without checking intent.getAction(), where the receiver is reachable by other apps.

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-360
Severity Low
SARIF level note
Kind sast
Languages Android, Java, Kotlin
CWE CWE-360
CAPEC CAPEC-66
ATT&CK T1190
CWSS vector CWSS:1.0/TI:L/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:L/DI:H/EX:L/EC:N/P:L
Tags trust-system-event android broadcast-receiver cwe-360
Source internal/sast/rules/vnx-360.rego

Overview

VNX-360 maps to CWE-360: Trust of System Event Data. Only the system can broadcast BOOT_COMPLETED, SMS_RECEIVED or POWER_CONNECTED, but any installed app can deliver an intent straight to an exported receiver by naming its component. A receiver that acts without checking the intent’s action therefore treats a forged “system event”, and its extras, as genuine.

The rule reports, in Java and Kotlin, an onReceive(Context, Intent) method whose body (up to 40 lines) never reads the intent’s action (getAction(), intent.action, intent?.action), when the receiver is:

  • registered in the same file with an IntentFilter(...) / addAction(...) naming a system action (Intent.ACTION_*, "android.intent.action.*", "android.provider.Telephony.*", Telephony.Sms.Intents.*, *_ACTION constants), or
  • declared in an AndroidManifest.xml <receiver> whose intent filter names an android.* system action and which is neither android:exported="false" nor protected by android:permission.

Not reported: receivers that check the action, receivers for the app’s own custom actions only (VNX-ANDROID-003 judges exported components), receivers registered with RECEIVER_NOT_EXPORTED, non-exported or permission-protected manifest receivers, and test files.

What Gets Flagged

<receiver android:name=".sync.BootReceiver" android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>
// FLAGGED: any app can send this receiver an explicit intent with any extras
public void onReceive(Context context, Intent intent) {
    String account = intent.getStringExtra("account");
    SyncService.wipeAndResync(context, account);
}
// NOT FLAGGED
public void onReceive(Context context, Intent intent) {
    if (!Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
        return;
    }
    SyncService.schedule(context);
}

Remediation

  1. Check intent.getAction() against the expected system action before doing anything.
  2. Do not trust extras on a system-event intent; system broadcasts such as BOOT_COMPLETED carry none.
  3. Register dynamic receivers with RECEIVER_NOT_EXPORTED, and protect SMS receivers with android:permission="android.permission.BROADCAST_SMS".

References

Suppressing this rule

Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):

# nosec vnx-360
// nosec vnx-360

Or record an ignore rule: vulnetix ignore add --rule vnx-360 --reason "...". See Suppressing findings.

Edit this pageSources: cli/internal/sast/rules/vnx-360.rego, cli/internal/sast/docs/vnx-360.md
esc
↑ ↓ move↵ openSearches in your browser. A key like autonomy.max or a code like threshold_required jumps straight to it.