homebridge-roborock-vacuum2 1.4.13 → 1.4.14
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 +5 -0
- package/package.json +1 -1
- package/roborockLib/lib/map/zones.js +40 -4
- package/roborockLib/lib/roborockAuth.js +14 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.4.14
|
|
4
|
+
- Hardened region detection by parsing the configured Roborock host instead of using substring matches.
|
|
5
|
+
- Sanitized map obstacle image URLs before assigning them in the browser UI to reduce XSS and client-side redirect risk.
|
|
6
|
+
- Added explicit read-only permissions to the CI workflow, upgraded GitHub Actions versions, and moved Codecov uploads to a repository secret.
|
|
7
|
+
|
|
3
8
|
## 1.4.13
|
|
4
9
|
- Adjusted `package.json` repository metadata to match the fork URL exactly for npm Trusted Publishing compatibility.
|
|
5
10
|
- Updated the npm publish workflow to use Node 24 and the latest npm CLI for Trusted Publishing compatibility.
|
package/package.json
CHANGED
|
@@ -42,6 +42,44 @@ window.onload = function () {
|
|
|
42
42
|
var largePhoto = document.getElementById("largePhoto");
|
|
43
43
|
var largePhotoImage = document.getElementById("largePhoto-image");
|
|
44
44
|
|
|
45
|
+
function getSafeImageSource(rawValue) {
|
|
46
|
+
if (typeof rawValue !== "string") {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const value = rawValue.trim();
|
|
51
|
+
if (!value) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (/^data:image\//i.test(value) || value.startsWith("blob:")) {
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
const imageURL = new URL(value, window.location.origin);
|
|
61
|
+
if (!["http:", "https:"].includes(imageURL.protocol)) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
if (imageURL.hostname !== window.location.hostname) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
return imageURL.toString();
|
|
68
|
+
} catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function applySafeImageSource(imageElement, rawValue) {
|
|
74
|
+
const safeImageSource = getSafeImageSource(rawValue);
|
|
75
|
+
if (!safeImageSource) {
|
|
76
|
+
console.warn("Ignoring unsafe obstacle image source");
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
imageElement.src = safeImageSource;
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
|
|
45
83
|
var socket = new WebSocket("ws://" + window.location.hostname + ":7906");
|
|
46
84
|
|
|
47
85
|
socket.onopen = () => {
|
|
@@ -292,8 +330,7 @@ window.onload = function () {
|
|
|
292
330
|
socket.onmessage = function (event) {
|
|
293
331
|
const serverData = JSON.parse(event.data);
|
|
294
332
|
|
|
295
|
-
if (serverData.image) {
|
|
296
|
-
popupImage.src = serverData.image;
|
|
333
|
+
if (serverData.image && applySafeImageSource(popupImage, serverData.image)) {
|
|
297
334
|
socket.onmessage = null;
|
|
298
335
|
|
|
299
336
|
popupImage.addEventListener("click", function () {
|
|
@@ -312,10 +349,9 @@ window.onload = function () {
|
|
|
312
349
|
socket.onmessage = function (event) {
|
|
313
350
|
const serverData = JSON.parse(event.data);
|
|
314
351
|
|
|
315
|
-
if (serverData.image) {
|
|
352
|
+
if (serverData.image && applySafeImageSource(largePhotoImage, serverData.image)) {
|
|
316
353
|
popup.style.display = "none";
|
|
317
354
|
largePhoto.style.display = "block";
|
|
318
|
-
largePhotoImage.src = serverData.image;
|
|
319
355
|
|
|
320
356
|
largePhotoImage.onclick = function () {
|
|
321
357
|
largePhoto.style.display = "none";
|
|
@@ -22,18 +22,27 @@ function normalizeBaseURL(baseURL) {
|
|
|
22
22
|
return baseURL.replace(/^https?:\/\//i, "").replace(/\/+$/, "");
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
function parseHostname(baseURL) {
|
|
26
|
+
try {
|
|
27
|
+
return new URL(`https://${normalizeBaseURL(baseURL)}`).hostname.toLowerCase();
|
|
28
|
+
} catch {
|
|
29
|
+
return normalizeBaseURL(baseURL).split("/")[0].split(":")[0].toLowerCase();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
25
33
|
function getRegionConfig(baseURL) {
|
|
26
|
-
const
|
|
27
|
-
|
|
34
|
+
const hostname = parseHostname(baseURL);
|
|
35
|
+
const labels = hostname.split(".").filter(Boolean);
|
|
36
|
+
if (labels.includes("euiot")) {
|
|
28
37
|
return { country: "DE", countryCode: "49" };
|
|
29
38
|
}
|
|
30
|
-
if (
|
|
39
|
+
if (labels.includes("usiot")) {
|
|
31
40
|
return { country: "US", countryCode: "1" };
|
|
32
41
|
}
|
|
33
|
-
if (
|
|
42
|
+
if (labels.includes("cniot")) {
|
|
34
43
|
return { country: "CN", countryCode: "86" };
|
|
35
44
|
}
|
|
36
|
-
if (
|
|
45
|
+
if (hostname === "api.roborock.com") {
|
|
37
46
|
return { country: "SG", countryCode: "65" };
|
|
38
47
|
}
|
|
39
48
|
return { country: "US", countryCode: "1" };
|