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>)

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.
References
Summary
Appium's base-driver mounts the built-in
/test/guinea-pig,/test/guinea-pig-scrollableand/test/guinea-pig-app-bannerroutes unconditionally on every server. The handler reflects thethrowErrorquery param, thecommentsPOST field, and theUser-Agentrequest header into the returned HTML viacompileLodashTemplate, which interpolates<%= expr %>asString(expr)with no HTML/JS escaping. This yields reflected XSS, and thethrowErrorvalue 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 is0.0.0.0.Details
Affected
@appium/base-driver10.6.0 (with Appium server 3.5.0); tested live.@appium/base-driverlib/utils.tscompileLodashTemplate.Location (file:line)
base-driver/lib/express/server.ts:216-219(
app.all('/test/guinea-pig', guineaPig)etc.).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'].base-driver/lib/utils.ts:67-83(compileLodashTemplate)emits
<%= expr %>asString(${expr})vianew Function(...), no escaping.base-driver/static/test/guinea-pig.html:11-12(
throwErrorinside<script>),:50(comment),:87(userAgent); same inguinea-pig-scrollable.html/guinea-pig-app-banner.html.PoC
Requests:
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.References