#14578·omi

[Bounty proposal] security: omi-dropbox-app reflects raw uid into setup-page HTML (reflected XSS) + stored folder_name sink ($75 proposed)

Author: t3ratechCreated Sep 18, 2026Updated Sep 18, 2026

Symptom

plugins/omi-dropbox-app/main.pyGET / is unauthenticated and reflects the uid query parameter, unescaped, into the HTML it returns. Opening

https://<dropbox-app-host>/?uid=%22%3E%3Cscript%3Ealert(1)%3C/script%3E

returns markup whose retry link is

xml
<a href="/auth/dropbox?uid="><script>alert(1)</script>" class="btn">Connect Dropbox</a>

so the script runs in the victim's browser on the plugin's origin (reflected XSS). No login or prior state is needed — the disconnected branch renders for any uid with no stored tokens, which is every attacker-chosen value. A benign & or # in uid corrupts the link the same way.

Second sink: stored, not just reflected

folder_name is set by the user through POST /settings and rendered straight back into an attribute on the same page:

python
<input type="text" id="folder_name" name="folder_name" value="{settings.get('folder_name', 'Omi Conversations')}" ...>

A folder_name containing "> closes the attribute and the tag. Unlike the uid case this persists in the user's stored settings and fires on every subsequent page load.

display_name and email (from the Dropbox API) and the /auth/dropbox/callback error page — which interpolates str(e) raw into HTML — have the same shape.

Evidence

plugins/omi-dropbox-app/main.py on current main: the module imports no escaping helper at all (no html, no quote), and get_home_page_html() interpolates {uid} into three attributes (/settings, /disconnect, /auth/dropbox), plus the value= sink above.

Relationship to #13658

#13658 fixes exactly this class — "encode reflected uid in setup/auth pages" — across six apps: omi-clickup-app, omi-github-app, omi-notion-app, omi-slack-app, omi-twitter-chat-tools-app, omi-whoop-app. omi-dropbox-app was not in that set and still carries the defect.

Acceptance criteria

  1. uid is percent-encoded (quote(uid, safe="")) everywhere it reaches a URL — the three HTML attributes and the three RedirectResponse targets.
  2. folder_name, display_name, email, and the callback error text are HTML-escaped.
  3. /?uid=abc123 still renders /auth/dropbox?uid=abc123 unchanged.
  4. A registered hermetic regression suite proves red→green against the pre-fix module.

Fix

PR pending — implemented in the same session as this report, so nothing here is left as an unclaimed specification.

Bounty

$75 proposed. For comparables: #14350 proposes $25 for a single reflected-XSS sink in omi-twitter-app, while the plugin security issues in the current batch (#14430, #14442, #14444, #14446, #14451, #14453) are proposed at $150–$200. This one covers a reflected sink on an unauthenticated route, a stored sink, and four more, with a registered regression suite — so it sits between the two. Your number is the number.