Skip to content

Clipboard suggestion: mask based on content patterns (OTP/CC/JWT/seed/secret)#2491

Open
dsremo wants to merge 1 commit into
HeliBorg:mainfrom
dsremo:dsremo/clipboard-content-masking
Open

Clipboard suggestion: mask based on content patterns (OTP/CC/JWT/seed/secret)#2491
dsremo wants to merge 1 commit into
HeliBorg:mainfrom
dsremo:dsremo/clipboard-content-masking

Conversation

@dsremo
Copy link
Copy Markdown

@dsremo dsremo commented May 13, 2026

Summary

isClipSensitive() currently only returns true when the target input field declares inputType=textPassword or the OS-side ClipDescription sensitivity flag is set. That leaves OTPs, credit-card numbers, BIP-39 seed phrases, JWTs, and PEM private keys visible in plaintext on the suggestion strip whenever the user is pasting into a normal text field (chat composer, email body, search bar) — which is the most common shoulder-surf risk in practice.

This PR extends isClipSensitive() to additionally inspect the clipboard content itself, returning true (and therefore masking the visible suggestion to ***...***) if the content matches any of:

Pattern Matches
P_OTP Standalone 4–8 digit run (whole-string match)
P_CREDIT_CARD 13–19 digit run with optional spaces/dashes
P_JWT eyJ…\.<base64url>\.<base64url>
P_LONG_HEX Hex string ≥ 32 chars (SHA-256, API tokens, hex-encoded keys)
P_SEED_PHRASE 12–24 lowercase 3–8 char words space-separated (BIP-39 style)
P_SECRET_KEYWORD password, passwd, secret, api_key, access_token, bearer, private_key, ssh-rsa, ssh-ed25519, PEM private key block

Behavior

  • The masking only affects the visible suggestion strip text. The actual paste still uses the real content via latinIME.onTextInput(content.toString()) — so functionality is unchanged for the user, they just don't see a 6-digit OTP rendered in 18pt next to whoever might be looking over their shoulder.
  • 5000-char ceiling on the regex scan to avoid pathological clipboard payloads (e.g., full HTML pages on the clipboard) hitting the regex engine on every keystroke.
  • Pattern list is intentionally conservative — false positives just mean "user sees *** instead of the value for ~3 minutes of clipboard freshness", false negatives mean the value is visible.

Files touched

  • app/src/main/java/helium314/keyboard/latin/ClipboardHistoryManager.kt (+25 / −1)

Tested

Built and installed on Android 16 (HeliBoard_3.9-debug.apk). Verified manually with sample clipboard contents matching each pattern.

Notes

No new permissions. No new strings (the existing *-repeat path is reused). No settings — this is pure defense-in-depth on an existing privacy-sensitive code path. If you'd prefer this gated behind a setting, happy to add one.

…atterns

Currently isClipSensitive() only returns true when the target input field
declares inputType=textPassword (or the OS-side ClipDescription sensitivity
flag is set). That leaves OTPs, credit-card numbers, BIP-39 seed phrases,
JWTs, etc. visible in plaintext on the suggestion strip whenever the user
is pasting into a normal text field (chat composer, email body, search bar) -
which is the most common shoulder-surf risk.

This patch extends isClipSensitive() to additionally inspect the clipboard
content itself and mask the suggestion if it matches any of:

  - OTP: standalone 4-8 digit run (whole-string)
  - Credit card: 13-19 digit run with optional spaces/dashes
  - JWT: eyJ...\.<base64url>\.<base64url>
  - Long hex: hex string >=32 chars (SHA-256, API tokens, hex keys)
  - BIP-39-style seed phrase: 12-24 lowercase 3-8 char words
  - Secret keywords: password, passwd, secret, api_key, access_token,
    bearer, private_key, ssh-rsa, ssh-ed25519, PEM private key block

The masking only affects the visible suggestion strip text; the actual
paste still uses the real content via latinIME.onTextInput(content). 5000
char ceiling on the regex scan to avoid pathological clipboard payloads.

No new permissions, no UI changes, no settings - this is a pure
defense-in-depth tightening of an existing privacy-sensitive code path.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant