appium-mcp 1.86.3 → 1.86.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/ui/mcp-ui-utils.d.ts.map +1 -1
- package/dist/ui/mcp-ui-utils.js +53 -28
- package/dist/ui/mcp-ui-utils.js.map +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/ui/mcp-ui-utils.ts +59 -30
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## [1.86.4](https://github.com/appium/appium-mcp/compare/v1.86.3...v1.86.4) (2026-06-22)
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
|
|
5
|
+
* escape more ([#420](https://github.com/appium/appium-mcp/issues/420)) ([78acf26](https://github.com/appium/appium-mcp/commit/78acf260d62af82a83077d1d30a32e28a7714087))
|
|
6
|
+
|
|
1
7
|
## [1.86.3](https://github.com/appium/appium-mcp/compare/v1.86.2...v1.86.3) (2026-06-20)
|
|
2
8
|
|
|
3
9
|
### Miscellaneous Chores
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-ui-utils.d.ts","sourceRoot":"","sources":["../../src/ui/mcp-ui-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,GAClB;IACD,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE;QACR,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,EAAE,WAAW,CAAC;QACtB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH,CASA;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,KAAK,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC,EACF,QAAQ,EAAE,SAAS,GAAG,KAAK,EAC3B,UAAU,CAAC,EAAE,WAAW,GAAG,MAAM,GAChC,MAAM,
|
|
1
|
+
{"version":3,"file":"mcp-ui-utils.d.ts","sourceRoot":"","sources":["../../src/ui/mcp-ui-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,GAClB;IACD,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE;QACR,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,EAAE,WAAW,CAAC;QACtB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH,CASA;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,KAAK,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC,EACF,QAAQ,EAAE,SAAS,GAAG,KAAK,EAC3B,UAAU,CAAC,EAAE,WAAW,GAAG,MAAM,GAChC,MAAM,CA+LR;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,gBAAgB,EAAE,MAAM,EACxB,QAAQ,EAAE,MAAM,GACf,MAAM,CAwMR;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE;IACpD,SAAS,EAAE,MAAM,GAAG,GAAG,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GAAG,MAAM,CAiPT;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,KAAK,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC,GACD,MAAM,CAmMR;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CA0KtE;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,MAAM,EAAE,EAClB,cAAc,EAAE,MAAM,GAAG,IAAI,GAC5B,MAAM,CAsJR;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,KAAK,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GACrD,MAAM,CAgNR;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,MAAe,GACxB,MAAM,CAsJR;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,EAC7D,UAAU,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,GAC9C;IAAE,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,CAAA;CAAE,CAQzB"}
|
package/dist/ui/mcp-ui-utils.js
CHANGED
|
@@ -36,21 +36,28 @@ export function createDevicePickerUI(devices, platform, deviceType) {
|
|
|
36
36
|
: 'iOS Devices'
|
|
37
37
|
: 'Android Devices';
|
|
38
38
|
const deviceCards = devices
|
|
39
|
-
.map((device, index) =>
|
|
40
|
-
|
|
39
|
+
.map((device, index) => {
|
|
40
|
+
const udid = String(device.udid);
|
|
41
|
+
const name = device.name || udid;
|
|
42
|
+
const state = device.state ? String(device.state) : undefined;
|
|
43
|
+
const type = device.type ? String(device.type) : undefined;
|
|
44
|
+
const stateClass = state ? sanitizeClassName(state) : '';
|
|
45
|
+
return `
|
|
46
|
+
<div class="device-card" data-udid="${escapeHtml(udid)}" data-index="${index}">
|
|
41
47
|
<div class="device-header">
|
|
42
|
-
<h3>${
|
|
43
|
-
${
|
|
48
|
+
<h3>${escapeHtml(name)}</h3>
|
|
49
|
+
${state ? `<span class="device-state ${stateClass}">${escapeHtml(state)}</span>` : ''}
|
|
44
50
|
</div>
|
|
45
51
|
<div class="device-details">
|
|
46
|
-
<p><strong>UDID:</strong> <code>${
|
|
47
|
-
${
|
|
52
|
+
<p><strong>UDID:</strong> <code>${escapeHtml(udid)}</code></p>
|
|
53
|
+
${type ? `<p><strong>Type:</strong> ${escapeHtml(type)}</p>` : ''}
|
|
48
54
|
</div>
|
|
49
|
-
<button class="select-device-btn" onclick="selectDevice(
|
|
55
|
+
<button class="select-device-btn" data-udid="${escapeHtml(udid)}" onclick="selectDevice(this.dataset.udid)">
|
|
50
56
|
Select Device
|
|
51
57
|
</button>
|
|
52
58
|
</div>
|
|
53
|
-
|
|
59
|
+
`;
|
|
60
|
+
})
|
|
54
61
|
.join('');
|
|
55
62
|
return `
|
|
56
63
|
<!DOCTYPE html>
|
|
@@ -189,8 +196,8 @@ export function createDevicePickerUI(devices, platform, deviceType) {
|
|
|
189
196
|
payload: {
|
|
190
197
|
intent: 'select-device',
|
|
191
198
|
params: {
|
|
192
|
-
platform:
|
|
193
|
-
${deviceType ? `iosDeviceType:
|
|
199
|
+
platform: ${escapeScriptValue(platform)},
|
|
200
|
+
${deviceType ? `iosDeviceType: ${escapeScriptValue(deviceType)},` : ''}
|
|
194
201
|
deviceUdid: udid
|
|
195
202
|
}
|
|
196
203
|
}
|
|
@@ -217,6 +224,7 @@ export function createDevicePickerUI(devices, platform, deviceType) {
|
|
|
217
224
|
* @returns HTML string for screenshot viewer
|
|
218
225
|
*/
|
|
219
226
|
export function createScreenshotViewerUI(screenshotBase64, filepath) {
|
|
227
|
+
const downloadFilename = filepath.split('/').pop() || 'screenshot.png';
|
|
220
228
|
return `
|
|
221
229
|
<!DOCTYPE html>
|
|
222
230
|
<html lang="en">
|
|
@@ -340,7 +348,7 @@ export function createScreenshotViewerUI(screenshotBase64, filepath) {
|
|
|
340
348
|
<div class="toolbar">
|
|
341
349
|
<div class="toolbar-left">
|
|
342
350
|
<span style="font-size: 14px; font-weight: 500;">📸 Screenshot</span>
|
|
343
|
-
<span class="filepath">${filepath}</span>
|
|
351
|
+
<span class="filepath">${escapeHtml(filepath)}</span>
|
|
344
352
|
</div>
|
|
345
353
|
<div class="toolbar-right">
|
|
346
354
|
<button class="btn btn-secondary" onclick="downloadScreenshot()">Download</button>
|
|
@@ -348,7 +356,7 @@ export function createScreenshotViewerUI(screenshotBase64, filepath) {
|
|
|
348
356
|
</div>
|
|
349
357
|
</div>
|
|
350
358
|
<div class="image-container" id="imageContainer">
|
|
351
|
-
<img src="data:image/png;base64,${screenshotBase64}"
|
|
359
|
+
<img src="data:image/png;base64,${escapeHtml(screenshotBase64)}"
|
|
352
360
|
alt="Screenshot"
|
|
353
361
|
class="screenshot-img"
|
|
354
362
|
id="screenshotImg"
|
|
@@ -390,7 +398,7 @@ export function createScreenshotViewerUI(screenshotBase64, filepath) {
|
|
|
390
398
|
function downloadScreenshot() {
|
|
391
399
|
const link = document.createElement('a');
|
|
392
400
|
link.href = img.src;
|
|
393
|
-
link.download =
|
|
401
|
+
link.download = ${escapeScriptValue(downloadFilename)};
|
|
394
402
|
link.click();
|
|
395
403
|
}
|
|
396
404
|
|
|
@@ -429,6 +437,9 @@ export function createSessionDashboardUI(sessionInfo) {
|
|
|
429
437
|
const sessionIdDisplay = sessionIdStr.length > 8
|
|
430
438
|
? `${sessionIdStr.substring(0, 8)}...`
|
|
431
439
|
: sessionIdStr;
|
|
440
|
+
const deviceName = sessionInfo.deviceName;
|
|
441
|
+
const platformVersion = sessionInfo.platformVersion;
|
|
442
|
+
const udid = sessionInfo.udid;
|
|
432
443
|
return `
|
|
433
444
|
<!DOCTYPE html>
|
|
434
445
|
<html lang="en">
|
|
@@ -548,37 +559,37 @@ export function createSessionDashboardUI(sessionInfo) {
|
|
|
548
559
|
<div class="info-grid">
|
|
549
560
|
<div class="info-card">
|
|
550
561
|
<label>Session ID</label>
|
|
551
|
-
<value>${sessionIdDisplay}</value>
|
|
562
|
+
<value>${escapeHtml(sessionIdDisplay)}</value>
|
|
552
563
|
</div>
|
|
553
564
|
<div class="info-card">
|
|
554
565
|
<label>Platform</label>
|
|
555
|
-
<value>${sessionInfo.platform}</value>
|
|
566
|
+
<value>${escapeHtml(sessionInfo.platform)}</value>
|
|
556
567
|
</div>
|
|
557
568
|
<div class="info-card">
|
|
558
569
|
<label>Automation</label>
|
|
559
|
-
<value>${sessionInfo.automationName}</value>
|
|
570
|
+
<value>${escapeHtml(sessionInfo.automationName)}</value>
|
|
560
571
|
</div>
|
|
561
|
-
${
|
|
572
|
+
${deviceName
|
|
562
573
|
? `
|
|
563
574
|
<div class="info-card">
|
|
564
575
|
<label>Device</label>
|
|
565
|
-
<value>${
|
|
576
|
+
<value>${escapeHtml(deviceName)}</value>
|
|
566
577
|
</div>
|
|
567
578
|
`
|
|
568
579
|
: ''}
|
|
569
|
-
${
|
|
580
|
+
${platformVersion
|
|
570
581
|
? `
|
|
571
582
|
<div class="info-card">
|
|
572
583
|
<label>Platform Version</label>
|
|
573
|
-
<value>${
|
|
584
|
+
<value>${escapeHtml(platformVersion)}</value>
|
|
574
585
|
</div>
|
|
575
586
|
`
|
|
576
587
|
: ''}
|
|
577
|
-
${
|
|
588
|
+
${udid
|
|
578
589
|
? `
|
|
579
590
|
<div class="info-card">
|
|
580
591
|
<label>UDID</label>
|
|
581
|
-
<value><code style="font-size: 12px;">${
|
|
592
|
+
<value><code style="font-size: 12px;">${escapeHtml(udid)}</code></value>
|
|
582
593
|
</div>
|
|
583
594
|
`
|
|
584
595
|
: ''}
|
|
@@ -1182,17 +1193,17 @@ export function createContextSwitcherUI(contexts, currentContext) {
|
|
|
1182
1193
|
export function createAppListUI(apps) {
|
|
1183
1194
|
const appCards = apps
|
|
1184
1195
|
.map((app) => `
|
|
1185
|
-
<div class="app-card" data-package="${app.packageName}">
|
|
1196
|
+
<div class="app-card" data-package="${escapeHtml(app.packageName)}">
|
|
1186
1197
|
<div class="app-header">
|
|
1187
|
-
<h3>${app.appName || app.packageName}</h3>
|
|
1198
|
+
<h3>${escapeHtml(app.appName || app.packageName)}</h3>
|
|
1188
1199
|
</div>
|
|
1189
1200
|
<div class="app-details">
|
|
1190
|
-
<p><strong>Package:</strong> <code>${app.packageName}</code></p>
|
|
1201
|
+
<p><strong>Package:</strong> <code>${escapeHtml(app.packageName)}</code></p>
|
|
1191
1202
|
</div>
|
|
1192
1203
|
<div class="app-actions">
|
|
1193
|
-
<button class="btn btn-primary"
|
|
1194
|
-
<button class="btn btn-secondary"
|
|
1195
|
-
<button class="btn btn-danger"
|
|
1204
|
+
<button class="btn btn-primary" data-package="${escapeHtml(app.packageName)}" onclick="activateApp(this.dataset.package)">Activate</button>
|
|
1205
|
+
<button class="btn btn-secondary" data-package="${escapeHtml(app.packageName)}" onclick="terminateApp(this.dataset.package)">Terminate</button>
|
|
1206
|
+
<button class="btn btn-danger" data-package="${escapeHtml(app.packageName)}" onclick="uninstallApp(this.dataset.package)">Uninstall</button>
|
|
1196
1207
|
</div>
|
|
1197
1208
|
</div>
|
|
1198
1209
|
`)
|
|
@@ -1561,4 +1572,18 @@ function escapeHtml(value) {
|
|
|
1561
1572
|
.replace(/"/g, '"')
|
|
1562
1573
|
.replace(/'/g, ''');
|
|
1563
1574
|
}
|
|
1575
|
+
function escapeScriptValue(value) {
|
|
1576
|
+
return JSON.stringify(String(value))
|
|
1577
|
+
.replace(/</g, '\\u003C')
|
|
1578
|
+
.replace(/>/g, '\\u003E')
|
|
1579
|
+
.replace(/&/g, '\\u0026')
|
|
1580
|
+
.replace(/\u2028/g, '\\u2028')
|
|
1581
|
+
.replace(/\u2029/g, '\\u2029');
|
|
1582
|
+
}
|
|
1583
|
+
function sanitizeClassName(value) {
|
|
1584
|
+
return escapeHtml(String(value)
|
|
1585
|
+
.toLowerCase()
|
|
1586
|
+
.replace(/[^a-z0-9_-]+/g, '-')
|
|
1587
|
+
.replace(/^-+|-+$/g, ''));
|
|
1588
|
+
}
|
|
1564
1589
|
//# sourceMappingURL=mcp-ui-utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-ui-utils.js","sourceRoot":"","sources":["../../src/ui/mcp-ui-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAC9B,GAAW,EACX,WAAmB;IASnB,OAAO;QACL,IAAI,EAAE,UAAU;QAChB,QAAQ,EAAE;YACR,GAAG;YACH,QAAQ,EAAE,WAAW;YACrB,IAAI,EAAE,WAAW;SAClB;KACF,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAKE,EACF,QAA2B,EAC3B,UAAiC;IAEjC,MAAM,eAAe,GACnB,QAAQ,KAAK,KAAK,IAAI,UAAU;QAC9B,CAAC,CAAC,UAAU,KAAK,WAAW;YAC1B,CAAC,CAAC,gBAAgB;YAClB,CAAC,CAAC,aAAa;QACjB,CAAC,CAAC,iBAAiB,CAAC;IAExB,MAAM,WAAW,GAAG,OAAO;SACxB,GAAG,
|
|
1
|
+
{"version":3,"file":"mcp-ui-utils.js","sourceRoot":"","sources":["../../src/ui/mcp-ui-utils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/server.json
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
"name": "io.github.appium/appium-mcp",
|
|
4
4
|
"title": "MCP Appium - Mobile Development and Automation Server",
|
|
5
5
|
"description": "MCP server for Appium mobile automation on iOS and Android devices with test creation tools.",
|
|
6
|
-
"version": "1.86.
|
|
6
|
+
"version": "1.86.4",
|
|
7
7
|
"packages": [
|
|
8
8
|
{
|
|
9
9
|
"registryType": "npm",
|
|
10
10
|
"identifier": "appium-mcp",
|
|
11
|
-
"version": "1.86.
|
|
11
|
+
"version": "1.86.4",
|
|
12
12
|
"transport": {
|
|
13
13
|
"type": "stdio"
|
|
14
14
|
}
|
package/src/ui/mcp-ui-utils.ts
CHANGED
|
@@ -59,23 +59,29 @@ export function createDevicePickerUI(
|
|
|
59
59
|
: 'Android Devices';
|
|
60
60
|
|
|
61
61
|
const deviceCards = devices
|
|
62
|
-
.map(
|
|
63
|
-
(device
|
|
64
|
-
|
|
62
|
+
.map((device, index) => {
|
|
63
|
+
const udid = String(device.udid);
|
|
64
|
+
const name = device.name || udid;
|
|
65
|
+
const state = device.state ? String(device.state) : undefined;
|
|
66
|
+
const type = device.type ? String(device.type) : undefined;
|
|
67
|
+
const stateClass = state ? sanitizeClassName(state) : '';
|
|
68
|
+
|
|
69
|
+
return `
|
|
70
|
+
<div class="device-card" data-udid="${escapeHtml(udid)}" data-index="${index}">
|
|
65
71
|
<div class="device-header">
|
|
66
|
-
<h3>${
|
|
67
|
-
${
|
|
72
|
+
<h3>${escapeHtml(name)}</h3>
|
|
73
|
+
${state ? `<span class="device-state ${stateClass}">${escapeHtml(state)}</span>` : ''}
|
|
68
74
|
</div>
|
|
69
75
|
<div class="device-details">
|
|
70
|
-
<p><strong>UDID:</strong> <code>${
|
|
71
|
-
${
|
|
76
|
+
<p><strong>UDID:</strong> <code>${escapeHtml(udid)}</code></p>
|
|
77
|
+
${type ? `<p><strong>Type:</strong> ${escapeHtml(type)}</p>` : ''}
|
|
72
78
|
</div>
|
|
73
|
-
<button class="select-device-btn" onclick="selectDevice(
|
|
79
|
+
<button class="select-device-btn" data-udid="${escapeHtml(udid)}" onclick="selectDevice(this.dataset.udid)">
|
|
74
80
|
Select Device
|
|
75
81
|
</button>
|
|
76
82
|
</div>
|
|
77
|
-
|
|
78
|
-
)
|
|
83
|
+
`;
|
|
84
|
+
})
|
|
79
85
|
.join('');
|
|
80
86
|
|
|
81
87
|
return `
|
|
@@ -215,8 +221,8 @@ export function createDevicePickerUI(
|
|
|
215
221
|
payload: {
|
|
216
222
|
intent: 'select-device',
|
|
217
223
|
params: {
|
|
218
|
-
platform:
|
|
219
|
-
${deviceType ? `iosDeviceType:
|
|
224
|
+
platform: ${escapeScriptValue(platform)},
|
|
225
|
+
${deviceType ? `iosDeviceType: ${escapeScriptValue(deviceType)},` : ''}
|
|
220
226
|
deviceUdid: udid
|
|
221
227
|
}
|
|
222
228
|
}
|
|
@@ -247,6 +253,8 @@ export function createScreenshotViewerUI(
|
|
|
247
253
|
screenshotBase64: string,
|
|
248
254
|
filepath: string
|
|
249
255
|
): string {
|
|
256
|
+
const downloadFilename = filepath.split('/').pop() || 'screenshot.png';
|
|
257
|
+
|
|
250
258
|
return `
|
|
251
259
|
<!DOCTYPE html>
|
|
252
260
|
<html lang="en">
|
|
@@ -370,7 +378,7 @@ export function createScreenshotViewerUI(
|
|
|
370
378
|
<div class="toolbar">
|
|
371
379
|
<div class="toolbar-left">
|
|
372
380
|
<span style="font-size: 14px; font-weight: 500;">📸 Screenshot</span>
|
|
373
|
-
<span class="filepath">${filepath}</span>
|
|
381
|
+
<span class="filepath">${escapeHtml(filepath)}</span>
|
|
374
382
|
</div>
|
|
375
383
|
<div class="toolbar-right">
|
|
376
384
|
<button class="btn btn-secondary" onclick="downloadScreenshot()">Download</button>
|
|
@@ -378,7 +386,7 @@ export function createScreenshotViewerUI(
|
|
|
378
386
|
</div>
|
|
379
387
|
</div>
|
|
380
388
|
<div class="image-container" id="imageContainer">
|
|
381
|
-
<img src="data:image/png;base64,${screenshotBase64}"
|
|
389
|
+
<img src="data:image/png;base64,${escapeHtml(screenshotBase64)}"
|
|
382
390
|
alt="Screenshot"
|
|
383
391
|
class="screenshot-img"
|
|
384
392
|
id="screenshotImg"
|
|
@@ -420,7 +428,7 @@ export function createScreenshotViewerUI(
|
|
|
420
428
|
function downloadScreenshot() {
|
|
421
429
|
const link = document.createElement('a');
|
|
422
430
|
link.href = img.src;
|
|
423
|
-
link.download =
|
|
431
|
+
link.download = ${escapeScriptValue(downloadFilename)};
|
|
424
432
|
link.click();
|
|
425
433
|
}
|
|
426
434
|
|
|
@@ -470,6 +478,9 @@ export function createSessionDashboardUI(sessionInfo: {
|
|
|
470
478
|
sessionIdStr.length > 8
|
|
471
479
|
? `${sessionIdStr.substring(0, 8)}...`
|
|
472
480
|
: sessionIdStr;
|
|
481
|
+
const deviceName = sessionInfo.deviceName;
|
|
482
|
+
const platformVersion = sessionInfo.platformVersion;
|
|
483
|
+
const udid = sessionInfo.udid;
|
|
473
484
|
|
|
474
485
|
return `
|
|
475
486
|
<!DOCTYPE html>
|
|
@@ -590,42 +601,42 @@ export function createSessionDashboardUI(sessionInfo: {
|
|
|
590
601
|
<div class="info-grid">
|
|
591
602
|
<div class="info-card">
|
|
592
603
|
<label>Session ID</label>
|
|
593
|
-
<value>${sessionIdDisplay}</value>
|
|
604
|
+
<value>${escapeHtml(sessionIdDisplay)}</value>
|
|
594
605
|
</div>
|
|
595
606
|
<div class="info-card">
|
|
596
607
|
<label>Platform</label>
|
|
597
|
-
<value>${sessionInfo.platform}</value>
|
|
608
|
+
<value>${escapeHtml(sessionInfo.platform)}</value>
|
|
598
609
|
</div>
|
|
599
610
|
<div class="info-card">
|
|
600
611
|
<label>Automation</label>
|
|
601
|
-
<value>${sessionInfo.automationName}</value>
|
|
612
|
+
<value>${escapeHtml(sessionInfo.automationName)}</value>
|
|
602
613
|
</div>
|
|
603
614
|
${
|
|
604
|
-
|
|
615
|
+
deviceName
|
|
605
616
|
? `
|
|
606
617
|
<div class="info-card">
|
|
607
618
|
<label>Device</label>
|
|
608
|
-
<value>${
|
|
619
|
+
<value>${escapeHtml(deviceName)}</value>
|
|
609
620
|
</div>
|
|
610
621
|
`
|
|
611
622
|
: ''
|
|
612
623
|
}
|
|
613
624
|
${
|
|
614
|
-
|
|
625
|
+
platformVersion
|
|
615
626
|
? `
|
|
616
627
|
<div class="info-card">
|
|
617
628
|
<label>Platform Version</label>
|
|
618
|
-
<value>${
|
|
629
|
+
<value>${escapeHtml(platformVersion)}</value>
|
|
619
630
|
</div>
|
|
620
631
|
`
|
|
621
632
|
: ''
|
|
622
633
|
}
|
|
623
634
|
${
|
|
624
|
-
|
|
635
|
+
udid
|
|
625
636
|
? `
|
|
626
637
|
<div class="info-card">
|
|
627
638
|
<label>UDID</label>
|
|
628
|
-
<value><code style="font-size: 12px;">${
|
|
639
|
+
<value><code style="font-size: 12px;">${escapeHtml(udid)}</code></value>
|
|
629
640
|
</div>
|
|
630
641
|
`
|
|
631
642
|
: ''
|
|
@@ -1260,17 +1271,17 @@ export function createAppListUI(
|
|
|
1260
1271
|
const appCards = apps
|
|
1261
1272
|
.map(
|
|
1262
1273
|
(app) => `
|
|
1263
|
-
<div class="app-card" data-package="${app.packageName}">
|
|
1274
|
+
<div class="app-card" data-package="${escapeHtml(app.packageName)}">
|
|
1264
1275
|
<div class="app-header">
|
|
1265
|
-
<h3>${app.appName || app.packageName}</h3>
|
|
1276
|
+
<h3>${escapeHtml(app.appName || app.packageName)}</h3>
|
|
1266
1277
|
</div>
|
|
1267
1278
|
<div class="app-details">
|
|
1268
|
-
<p><strong>Package:</strong> <code>${app.packageName}</code></p>
|
|
1279
|
+
<p><strong>Package:</strong> <code>${escapeHtml(app.packageName)}</code></p>
|
|
1269
1280
|
</div>
|
|
1270
1281
|
<div class="app-actions">
|
|
1271
|
-
<button class="btn btn-primary"
|
|
1272
|
-
<button class="btn btn-secondary"
|
|
1273
|
-
<button class="btn btn-danger"
|
|
1282
|
+
<button class="btn btn-primary" data-package="${escapeHtml(app.packageName)}" onclick="activateApp(this.dataset.package)">Activate</button>
|
|
1283
|
+
<button class="btn btn-secondary" data-package="${escapeHtml(app.packageName)}" onclick="terminateApp(this.dataset.package)">Terminate</button>
|
|
1284
|
+
<button class="btn btn-danger" data-package="${escapeHtml(app.packageName)}" onclick="uninstallApp(this.dataset.package)">Uninstall</button>
|
|
1274
1285
|
</div>
|
|
1275
1286
|
</div>
|
|
1276
1287
|
`
|
|
@@ -1652,3 +1663,21 @@ function escapeHtml(value: unknown): string {
|
|
|
1652
1663
|
.replace(/"/g, '"')
|
|
1653
1664
|
.replace(/'/g, ''');
|
|
1654
1665
|
}
|
|
1666
|
+
|
|
1667
|
+
function escapeScriptValue(value: unknown): string {
|
|
1668
|
+
return JSON.stringify(String(value))
|
|
1669
|
+
.replace(/</g, '\\u003C')
|
|
1670
|
+
.replace(/>/g, '\\u003E')
|
|
1671
|
+
.replace(/&/g, '\\u0026')
|
|
1672
|
+
.replace(/\u2028/g, '\\u2028')
|
|
1673
|
+
.replace(/\u2029/g, '\\u2029');
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
function sanitizeClassName(value: unknown): string {
|
|
1677
|
+
return escapeHtml(
|
|
1678
|
+
String(value)
|
|
1679
|
+
.toLowerCase()
|
|
1680
|
+
.replace(/[^a-z0-9_-]+/g, '-')
|
|
1681
|
+
.replace(/^-+|-+$/g, '')
|
|
1682
|
+
);
|
|
1683
|
+
}
|