MobSF / MobSF/mobsfscan

android_kotlin_hiddenui fires as Critical/ERROR on any View.GONE/INVISIBLE, not just sensitive views — massive false-positive rate

Open Beginner friendly
#123 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
787
Forks
125
PR merge metrics
No merged PRs in 30d

Description

Title: android_kotlin_hiddenui fires as Critical/ERROR on any View.GONE/INVISIBLE, not just sensitive views — massive false-positive rate

Rule in question

https://github.com/MobSF/mobsfscan/blob/main/mobsfscan/rules/semgrep/kotlin/android.yaml#L2

id: android_kotlin_hiddenui
...
message: Hidden elements in view can be used to hide data from user. But this data can be leaked.
severity: ERROR
metadata:
  cwe: cwe-919
  owasp-mobile: m1
  masvs: storage-7

What the rule matches

$V.visibility = View.GONE, $V.visibility = View.INVISIBLE, the ternary variants, and $V.setVisibility(View.GONE|INVISIBLE) — for any $V. There's no constraint on what the view is or what it contains.

Why this is a problem

The underlying concern (CWE-919 / MASVS-STORAGE-7) is real but narrow: a view holding sensitive data (a PIN entry, a masked card number, an auth token displayed somewhere) that's merely hidden with GONE/INVISIBLE is still present in the view hierarchy and can be read back via accessibility services, uiautomator dumps, or similar tooling, even though a human eye can't see it.

But View.GONE/INVISIBLE is also just... how Android apps show and hide UI. Toggling visibility on dividers, chevrons, snackbars, tutorial overlays, and widget buttons is one of the most common UI operations in any nontrivial app. Because the pattern has no data-flow/naming constraint, it fires identically on:

// hides a layout divider — no data involved
headerDivider?.visibility = View.INVISIBLE

// hides a decorative chevron icon depending on form state
chevron.visibility = View.INVISIBLE

// hides a snackbar view before/after animating it in
snackBarView.visibility = View.INVISIBLE

as it would on an actual hidden sensitive field. In a real-world Android codebase we scanned, 100% of the android_kotlin_hiddenui hits were benign visibility toggles (icons, dividers, snackbars, home-screen widget buttons) — zero were sensitive-data related — yet every one is reported as Critical/ERROR severity, identical to genuine hardcoded-secret or world-writable-file findings. This makes the rule effectively unusable for triage: it either gets ignored wholesale or burns significant analyst time confirming non-issues.

Comparison to other rules in the same file

Two other rules in this exact file already solve this problem correctly by scoping to sensitive names via metavariable-regex:

  • android_kotlin_hardcoded — restricts to variables matching (?i)^(?:password|pass|username|secret|key|(?:api|secret|private|access|encryption|auth)_?key)$
  • android_kotlin_sensitive_input_keyboard_cache — restricts to fields matching (?i).*(?:password|passcode|pin|secret|otp|token).*
  • android_kotlin_sensitive_notification — restricts notification content to the same sensitive-name pattern

android_kotlin_hiddenui is the outlier: it's the only "sensitive data exposure via UI" rule in the file with no such scoping.

There is no legitimate code change that satisfies this rule

We looked at what it would actually take to make a flagged line pass this check as currently written, without suppressing the finding. Because the pattern is a pure literal-token match (not a data-flow or type-aware check), there are exactly two ways to make it stop firing on a given line, and neither is a real remediation:

  1. Replace the named constant with its raw integer value. View.INVISIBLE is 4 and View.GONE is 8 in the Android SDK, so:

    // before — matches android_kotlin_hiddenui
    headerDivider?.visibility = View.INVISIBLE
    
    // after — does not match; identical behavior at runtime
    headerDivider?.visibility = 4  // View.INVISIBLE
    

    This compiles and behaves identically. The view is hidden in exactly the same way, exactly as "leakable" by whatever mechanism the rule is theoretically worried about, as before. The only thing that changed is that the scanner's string match no longer applies. This is textbook scanner evasion, not a fix — arguably it makes the code worse (magic number instead of the SDK constant) for zero security benefit.

  2. Delete the visibility-toggle code entirely. This passes the check but removes the underlying UI feature (the divider/chevron/snackbar would always be shown), i.e. a functional regression traded for a clean scan.

There is no third option where a code change makes the app meaningfully more secure and the rule stops firing as a result, because the rule isn't measuring anything about sensitive data handling — it's matching on the presence of an API call. For this specific rule, "the scan is green" and "the code is secure" are uncorrelated, so gating CI/release on it produces either permanent noise or incentivizes exactly the kind of cosmetic rewrite in option 1 above. That's arguably the strongest argument for fixing the rule itself rather than asking downstream projects to work around it.

Suggested fix

Apply the same metavariable-regex approach already used elsewhere in this file, constraining $V (or the receiver's declared/inferred name) to sensitive-sounding identifiers, e.g.:

metavariable-regex:
  metavariable: $V
  regex: "(?i).*(?:password|passcode|pin|cvv|card(?:num|number)?|secret|token|otp|auth).*"

This alone would eliminate the vast majority of false positives while preserving detection for the actual risk scenario (hiding a sensitive-data view instead of clearing/removing it).

As a secondary/complementary option, consider dropping severity from ERROR to WARNING/INFO given how broad and heuristic the current pattern is, similar to how other high-noise SAST checks are often kept at lower severity until a stronger signal (sensitive naming, adjacency to a password/PIN EditText, etc.) is present.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in rules/semgrep/kotlin/android.yaml at android_kotlin_hiddenui and compare its metavariable-regex scoping with the neighboring sensitive rules. Check the rule against the benign visibility examples and sensitive-looking view names described in the issue. Done means common non-sensitive toggles no longer report while the stated sensitive-view case remains covered, with the severity choice resolved.

Written by the indexing model from the issue text.

Assessment

Tech stack
kotlin
Domain
mobile-dev, security
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.