GCVE Workshop - 22 September 2026 (14:00-18:00), Luxembourg Before The Vulnopticon Conference - Registration

GHSA-3WGP-X9P5-C7CC

Vulnerability from github – Published: 2026-09-01 18:54 – Updated: 2026-09-01 18:54
VLAI
Summary
Appium: Reflected XSS / arbitrary JS in @appium/base-driver /test/guinea-pig* routes
Details

Summary

Appium's base-driver mounts the built-in /test/guinea-pig, /test/guinea-pig-scrollable and /test/guinea-pig-app-banner routes unconditionally on every server. The handler reflects the throwError query param, the comments POST field, and the User-Agent request header into the returned HTML via compileLodashTemplate, which interpolates <%= expr %> as String(expr) with no HTML/JS escaping. This yields reflected XSS, and the throwError value is reflected inside a <script> block, giving arbitrary JavaScript execution on the server's origin. No authentication, no session, no driver and no plugin are required, and the default bind address is 0.0.0.0.

Details

Affected

  • @appium/base-driver 10.6.0 (with Appium server 3.5.0); tested live.
  • Template engine helper: @appium/base-driver lib/utils.ts compileLodashTemplate.

Location (file:line)

  • Routes mounted unconditionally: base-driver/lib/express/server.ts:216-219 (app.all('/test/guinea-pig', guineaPig) etc.).
  • Tainting: base-driver/lib/express/static.ts:35-61 (guineaPigTemplate) — throwError = String(req.params.throwError ?? req.query?.throwError), params.comment = String(req.body.comments), params.userAgent = req.headers['user-agent'].
  • Unescaped render: base-driver/lib/utils.ts:67-83 (compileLodashTemplate) emits <%= expr %> as String(${expr}) via new Function(...), no escaping.
  • Sinks (shipped templates): base-driver/static/test/guinea-pig.html:11-12 (throwError inside <script>), :50 (comment), :87 (userAgent); same in guinea-pig-scrollable.html / guinea-pig-app-banner.html.

PoC

Requests:

GET /test/guinea-pig?throwError=x%27%2balert(document.domain)%2b%27
POST /test/guinea-pig         (body: comments=</span><img src=x onerror=alert(1)>)
GET  /test/guinea-pig         (header: User-Agent: <script>alert(7)</script>)

image

Impact

An attacker who can get a victim to open a crafted link (or auto-submit a form) to the Appium server executes arbitrary JavaScript on the server's origin. With default CORS * + no authentication, that JS can drive the WebDriver REST API and plugin endpoints. The endpoints are debug/test fixtures that should not be reachable on a production listener at all.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 10.6.0"
      },
      "package": {
        "ecosystem": "npm",
        "name": "@appium/base-driver"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "10.7.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-58191"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-489",
      "CWE-79"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-09-01T18:54:53Z",
    "nvd_published_at": "2026-07-08T21:16:52Z",
    "severity": "MODERATE"
  },
  "details": "## Summary\n\nAppium\u0027s base-driver mounts the built-in `/test/guinea-pig`, `/test/guinea-pig-scrollable` and `/test/guinea-pig-app-banner` routes **unconditionally** on every server. The handler reflects the `throwError` query param, the `comments` POST field, and the `User-Agent` request header into the returned HTML via `compileLodashTemplate`, which interpolates `\u003c%= expr %\u003e` as `String(expr)` with **no HTML/JS escaping**. This yields reflected XSS, and the `throwError` value is reflected **inside a `\u003cscript\u003e` block**, giving arbitrary JavaScript execution on the server\u0027s origin. No authentication, no session, no driver and no plugin are required, and the default bind address is `0.0.0.0`.\n\n## Details\n\n### Affected\n- `@appium/base-driver` **10.6.0** (with Appium server **3.5.0**); tested live.\n- Template engine helper: `@appium/base-driver` `lib/utils.ts` `compileLodashTemplate`.\n\n### Location (file:line)\n- Routes mounted unconditionally: `base-driver/lib/express/server.ts:216-219`\n(`app.all(\u0027/test/guinea-pig\u0027, guineaPig)` etc.).\n- Tainting: `base-driver/lib/express/static.ts:35-61` (`guineaPigTemplate`) \u2014\n`throwError = String(req.params.throwError ?? req.query?.throwError)`, `params.comment = String(req.body.comments)`, `params.userAgent = req.headers[\u0027user-agent\u0027]`.\n- Unescaped render: `base-driver/lib/utils.ts:67-83` (`compileLodashTemplate`)\nemits `\u003c%= expr %\u003e` as `String(${expr})` via `new Function(...)`, no escaping.\n- Sinks (shipped templates): `base-driver/static/test/guinea-pig.html:11-12`\n(`throwError` inside `\u003cscript\u003e`), `:50` (`comment`), `:87` (`userAgent`); same in `guinea-pig-scrollable.html` / `guinea-pig-app-banner.html`.\n\n\n### PoC\n\nRequests:\n```\nGET /test/guinea-pig?throwError=x%27%2balert(document.domain)%2b%27\nPOST /test/guinea-pig         (body: comments=\u003c/span\u003e\u003cimg src=x onerror=alert(1)\u003e)\nGET  /test/guinea-pig         (header: User-Agent: \u003cscript\u003ealert(7)\u003c/script\u003e)\n```\n\n\u003cimg width=\"973\" height=\"276\" alt=\"image\" src=\"https://github.com/user-attachments/assets/f58e1dce-f3ad-43d3-b66e-1ff5efea3866\" /\u003e\n\n\n### Impact\n\nAn attacker who can get a victim to open a crafted link (or auto-submit a form) to the Appium server executes arbitrary JavaScript on the server\u0027s origin. With default CORS `*` + no authentication, that JS can drive the WebDriver REST API and plugin endpoints. The endpoints are debug/test fixtures that should not be reachable on a production listener at all.",
  "id": "GHSA-3wgp-x9p5-c7cc",
  "modified": "2026-09-01T18:54:53Z",
  "published": "2026-09-01T18:54:53Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/appium/appium/security/advisories/GHSA-3wgp-x9p5-c7cc"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-58191"
    },
    {
      "type": "WEB",
      "url": "https://github.com/appium/appium/pull/22394"
    },
    {
      "type": "WEB",
      "url": "https://github.com/appium/appium/commit/d94a40af9f8040191ee7888571a1c9d5aec59f89"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/appium/appium"
    },
    {
      "type": "WEB",
      "url": "https://github.com/appium/appium/releases/tag/@appium/base-driver@10.7.0"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Appium: Reflected XSS / arbitrary JS in @appium/base-driver /test/guinea-pig* routes"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Loading…