Skip to content

Commit 4d74a56

Browse files
authored
🐛 对齐 PR#1319, 防止参数类型出错 (#1320)
* 对齐 PR#1319, 防止参数类型出错 * 写法修改 * 写法修改 * 写法修改 * typo * 先显式校验 details 为非 null 的 plain object * `Object.entries` 的 key 不用转
1 parent 950d7f7 commit 4d74a56

5 files changed

Lines changed: 200 additions & 92 deletions

File tree

src/app/service/content/gm_api/gm_api.ts

Lines changed: 45 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ export default class GMApi extends GM_Base {
267267
}
268268

269269
static _GM_setValue(a: GMApi, promise: any, key: string, value: any) {
270+
key = `${key}`;
270271
if (!a.scriptRes) return;
271272
if (valChangeCounterId > 1e8) {
272273
// 防止 valChangeCounterId 过大导致无法正常工作
@@ -498,7 +499,7 @@ export default class GMApi extends GM_Base {
498499
if (typeof message !== "string") {
499500
message = Native.jsonStringify(message);
500501
}
501-
this.sendMessage("GM_log", [message, level, labels]);
502+
this.sendMessage("GM_log", [`${message}`, `${level}`, labels]);
502503
}
503504

504505
@GMContext.API({ depend: ["GM_log"] })
@@ -521,7 +522,7 @@ export default class GMApi extends GM_Base {
521522
// 辅助GM_xml获取blob数据
522523
@GMContext.API()
523524
public CAT_fetchBlob(url: string): Promise<Blob> {
524-
return this.sendMessage("CAT_fetchBlob", [url]);
525+
return this.sendMessage("CAT_fetchBlob", [`${url}`]);
525526
}
526527

527528
@GMContext.API()
@@ -540,17 +541,25 @@ export default class GMApi extends GM_Base {
540541
details: GMTypes.CookieDetails,
541542
done: (cookie: GMTypes.Cookie[] | any, error: any | undefined) => void
542543
) {
543-
// 如果url和域名都没有,自动填充当前url
544-
if (!details.url && !details.domain) {
545-
details.url = window.location.href;
546-
}
547-
// 如果是set、delete操作,自动填充当前url
548-
if (action === "set" || action === "delete") {
549-
if (!details.url) {
550-
details.url = window.location.href;
551-
}
544+
// 防止错误参数类型传送
545+
if (
546+
typeof (details || false) !== "object" ||
547+
typeof (details.domain ?? "") !== "string" ||
548+
typeof (details.expirationDate ?? 0) !== "number" ||
549+
typeof (details.httpOnly ?? false) !== "boolean" ||
550+
typeof (details.name ?? "") !== "string" ||
551+
typeof (details.partitionKey ?? null) !== "object" ||
552+
typeof (details.path ?? "") !== "string" ||
553+
typeof (details.secure ?? false) !== "boolean" ||
554+
typeof (details.session ?? false) !== "boolean" ||
555+
typeof (details.url ?? "") !== "string" ||
556+
typeof (details.value ?? "") !== "string"
557+
) {
558+
done(undefined, new Error("Invalid Argument Type"));
559+
return;
552560
}
553-
a.sendMessage("GM_cookie", [action, details])
561+
// 确保物件参数可以传送
562+
a.sendMessage("GM_cookie", [`${action}`, customClone(details)])
554563
.then((resp: any) => {
555564
done && done(resp, undefined);
556565
})
@@ -707,7 +716,7 @@ export default class GMApi extends GM_Base {
707716
this.EE.addListener("menuClick:" + menuKey, listener);
708717
}
709718
// 发送至 service worker 处理(唯一键,显示名字,不包括id的其他设定)
710-
this.sendMessage("GM_registerMenuCommand", [menuKey, name, options] as GMRegisterMenuCommandParam);
719+
this.sendMessage("GM_registerMenuCommand", [menuKey, `${name}`, options] as GMRegisterMenuCommandParam);
711720
return ret;
712721
}
713722

@@ -889,27 +898,29 @@ export default class GMApi extends GM_Base {
889898
const url = await toBlobURL(this, details.data);
890899
sendDetails.data = url;
891900
}
892-
this.sendMessage("CAT_fileStorage", [action, sendDetails]).then(async (resp: { action: string; data: any }) => {
893-
switch (resp.action) {
894-
case "onload": {
895-
if (action === "download") {
896-
// 读取blob
897-
const blob = await this.CAT_fetchBlob(resp.data);
898-
details.onload && details.onload(blob);
899-
} else {
900-
details.onload && details.onload(resp.data);
901+
this.sendMessage("CAT_fileStorage", [`${action}`, sendDetails]).then(
902+
async (resp: { action: string; data: any }) => {
903+
switch (resp.action) {
904+
case "onload": {
905+
if (action === "download") {
906+
// 读取blob
907+
const blob = await this.CAT_fetchBlob(resp.data);
908+
details.onload && details.onload(blob);
909+
} else {
910+
details.onload && details.onload(resp.data);
911+
}
912+
break;
901913
}
902-
break;
903-
}
904-
case "error": {
905-
if (typeof resp.data.code === "undefined") {
906-
details.onerror && details.onerror({ code: -1, message: resp.data.message });
907-
return;
914+
case "error": {
915+
if (typeof resp.data.code === "undefined") {
916+
details.onerror && details.onerror({ code: -1, message: resp.data.message });
917+
return;
918+
}
919+
details.onerror && details.onerror(resp.data);
908920
}
909-
details.onerror && details.onerror(resp.data);
910921
}
911922
}
912-
});
923+
);
913924
}
914925

915926
// 用于脚本跨域请求,需要@connect domain指定允许的域名
@@ -1199,7 +1210,7 @@ export default class GMApi extends GM_Base {
11991210
if (typeof data.tag === "string") {
12001211
notificationId = notificationTagMap.get(data.tag);
12011212
}
1202-
gmApi.sendMessage("GM_notification", [data, notificationId]).then((id) => {
1213+
gmApi.sendMessage("GM_notification", [customClone(data), notificationId]).then((id) => {
12031214
if (!gmApi.EE) return;
12041215
if (create) {
12051216
create.apply({ id }, [id]);
@@ -1292,13 +1303,13 @@ export default class GMApi extends GM_Base {
12921303
// ScriptCat 额外API
12931304
@GMContext.API({ alias: "GM.closeNotification" })
12941305
public GM_closeNotification(id: string): void {
1295-
this.sendMessage("GM_closeNotification", [id]);
1306+
this.sendMessage("GM_closeNotification", [`${id}`]);
12961307
}
12971308

12981309
// ScriptCat 额外API
12991310
@GMContext.API({ alias: "GM.updateNotification" })
13001311
public GM_updateNotification(id: string, details: GMTypes.NotificationDetails): void {
1301-
this.sendMessage("GM_updateNotification", [id, details]);
1312+
this.sendMessage("GM_updateNotification", [`${id}`, customClone(details)]);
13021313
}
13031314

13041315
@GMContext.API({ depend: ["GM_closeInTab"] })
@@ -1439,7 +1450,7 @@ export default class GMApi extends GM_Base {
14391450
// 参考: https://github.com/Tampermonkey/tampermonkey/issues/1250
14401451
let mimetype: string | undefined;
14411452
if (typeof info === "object" && info?.mimetype) {
1442-
mimetype = info.mimetype;
1453+
mimetype = `${info.mimetype}`;
14431454
} else {
14441455
mimetype = (typeof info === "string" ? info : info?.type) || "text/plain";
14451456
if (mimetype === "text") mimetype = "text/plain";

src/app/service/content/gm_api/gm_xhr.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ export const convObjectToURL = async (object: string | URL | Blob | File | undef
9999

100100
export const urlToDocumentInContentPage = async (a: GMApi, url: string, isContent: boolean) => {
101101
// url (e.g. blob url) -> XMLHttpRequest (CONTENT) -> Document (CONTENT)
102-
const nodeId = await a.sendMessage("CAT_fetchDocument", [url, isContent]);
102+
const nodeId = await a.sendMessage("CAT_fetchDocument", [`${url}`, isContent]);
103103
return (<CustomEventMessage>a.message).getAndDelRelatedTarget(nodeId) as Document;
104104
};
105105

src/app/service/service_worker/gm_api/gm_api.ts

Lines changed: 78 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,15 @@ import type { ConfirmParam } from "../permission_verify";
1010
import PermissionVerify, { PermissionVerifyApiGet } from "../permission_verify";
1111
import { cacheInstance } from "@App/app/cache";
1212
import { type RuntimeService } from "../runtime";
13-
import { getIcon, isFirefox, getCurrentTab, openInCurrentTab, cleanFileName, makeBlobURL } from "@App/pkg/utils/utils";
13+
import {
14+
getIcon,
15+
isFirefox,
16+
getCurrentTab,
17+
openInCurrentTab,
18+
cleanFileName,
19+
makeBlobURL,
20+
stripUndefined,
21+
} from "@App/pkg/utils/utils";
1422
import { type SystemConfig } from "@App/pkg/config/config";
1523
import i18next, { i18nName } from "@App/locales/locales";
1624
import FileSystemFactory from "@Packages/filesystem/factory";
@@ -282,15 +290,18 @@ export default class GMApi {
282290
return true;
283291
}
284292
const detail = request.params[1];
285-
if (!detail.url && !detail.domain) {
286-
throw new Error("there must be one of url or domain");
293+
// 未指定 url 和 domain 时,自动使用当前页面的 URL(兼容 Tampermonkey 行为)
294+
const senderURL = sender.getSender()?.url;
295+
if (!detail.url && !detail.domain && senderURL) {
296+
detail.url = senderURL;
287297
}
288298
let url: URL = <URL>{};
289299
if (detail.url) {
290-
url = new URL(detail.url);
300+
url = new URL(`${detail.url}`);
301+
} else if (detail.domain) {
302+
url.hostname = url.host = `${detail.domain}`;
291303
} else {
292-
url.host = detail.domain || "";
293-
url.hostname = detail.domain || "";
304+
throw new Error("there must be one of url or domain");
294305
}
295306
if (getConnectMatched(request.script.metadata.connect, url, sender) === ConnectMatch.NONE) {
296307
// 检查是否配置了权限
@@ -322,17 +333,15 @@ export default class GMApi {
322333
if (param.length !== 2) {
323334
throw new Error("there must be two parameters");
324335
}
325-
const detail: GMTypes.CookieDetails = request.params[1];
326-
// url或者域名不能为空
327-
if (detail.url) {
328-
detail.url = detail.url.trim();
329-
}
330-
if (detail.domain) {
331-
detail.domain = detail.domain.trim();
332-
}
333-
if (!detail.url && !detail.domain) {
334-
throw new Error("there must be one of url or domain");
336+
const cookieAction: string = `${param[0]}`;
337+
const detail: GMTypes.CookieDetails = param[1];
338+
// 未指定 url 和 domain 时,自动使用当前页面的 URL(兼容 Tampermonkey 行为)
339+
const senderURL = sender.getSender()?.url;
340+
if (!detail.url && !detail.domain && senderURL) {
341+
detail.url = senderURL;
335342
}
343+
if (detail.domain) detail.domain = `${detail.domain}`.trim();
344+
if (detail.url) detail.url = `${detail.url}`.trim();
336345
if (!detail.partitionKey || typeof detail.partitionKey !== "object") {
337346
detail.partitionKey = {};
338347
}
@@ -350,48 +359,63 @@ export default class GMApi {
350359
storeId = store.id;
351360
}
352361
}
353-
switch (param[0]) {
362+
switch (cookieAction) {
354363
case "list": {
355-
const cookies = await chrome.cookies.getAll({
356-
domain: detail.domain,
357-
name: detail.name,
358-
path: detail.path,
359-
secure: detail.secure,
360-
session: detail.session,
361-
url: detail.url,
362-
storeId: storeId,
363-
partitionKey: detail.partitionKey,
364-
});
364+
detail.domain = detail.domain || undefined;
365+
detail.url = detail.url || undefined;
366+
const cookies = await chrome.cookies.getAll(
367+
stripUndefined({
368+
domain: detail.domain,
369+
name: detail.name,
370+
path: detail.path,
371+
secure: detail.secure,
372+
session: detail.session,
373+
url: detail.url,
374+
storeId: storeId,
375+
partitionKey: stripUndefined(detail.partitionKey),
376+
})
377+
);
365378
return cookies;
366379
}
367380
case "delete": {
381+
detail.domain = undefined;
382+
detail.url = detail.url || senderURL;
368383
if (!detail.url || !detail.name) {
369384
throw new Error("delete operation must have url and name");
370385
}
371-
await chrome.cookies.remove({
372-
name: detail.name,
373-
url: detail.url,
374-
storeId: storeId,
375-
partitionKey: detail.partitionKey,
376-
});
386+
await chrome.cookies.remove(
387+
stripUndefined({
388+
name: detail.name,
389+
url: detail.url,
390+
storeId: storeId,
391+
partitionKey: stripUndefined(detail.partitionKey),
392+
})
393+
);
377394
break;
378395
}
379396
case "set": {
380-
if (!detail.url || !detail.name || !detail.value) {
381-
throw new Error("set operation must have url, name and value");
397+
detail.domain = detail.domain || undefined;
398+
detail.url = detail.url || senderURL;
399+
// https://developer.chrome.com/docs/extensions/reference/api/cookies#method-set
400+
if (!detail.name) detail.name = ""; // Empty by default if omitted.
401+
if (!detail.value) detail.value = ""; // Empty by default if omitted.
402+
if (!detail.url) {
403+
throw new Error("set operation must have url");
382404
}
383-
await chrome.cookies.set({
384-
url: detail.url,
385-
name: detail.name,
386-
domain: detail.domain,
387-
value: detail.value,
388-
expirationDate: detail.expirationDate,
389-
path: detail.path,
390-
httpOnly: detail.httpOnly,
391-
secure: detail.secure,
392-
storeId: storeId,
393-
partitionKey: detail.partitionKey,
394-
});
405+
await chrome.cookies.set(
406+
stripUndefined({
407+
url: detail.url,
408+
name: detail.name,
409+
domain: detail.domain,
410+
value: detail.value,
411+
expirationDate: detail.expirationDate,
412+
path: detail.path,
413+
httpOnly: detail.httpOnly,
414+
secure: detail.secure,
415+
storeId: storeId,
416+
partitionKey: stripUndefined(detail.partitionKey),
417+
})
418+
);
395419
break;
396420
}
397421
default: {
@@ -614,16 +638,13 @@ export default class GMApi {
614638
}
615639
}
616640

617-
const cookies = await chrome.cookies.getAll({
618-
domain: undefined,
619-
name: undefined,
620-
path: undefined,
621-
secure: undefined,
622-
session: undefined,
623-
url: params.url,
624-
storeId: storeId,
625-
partitionKey: params.cookiePartition,
626-
});
641+
const cookies = await chrome.cookies.getAll(
642+
stripUndefined({
643+
url: params.url,
644+
storeId: storeId,
645+
partitionKey: stripUndefined(params.cookiePartition),
646+
})
647+
);
627648
// 追加cookie
628649
if (cookies?.length) {
629650
const v = cookies.map((c) => `${c.name}=${c.value}`).join("; ");

0 commit comments

Comments
 (0)