Skip to content

Commit b34f3ea

Browse files
CodFrmCopilot
andauthored
🐛 修复 脚本设置-授权管理 控制无效的问题 (#1267)
* 🐛 修复 脚本设置-授权管理 控制无效的问题 * 将校验逻辑放到confirm * 修复GM cookie权限判断 * 删除directDeny * buildCacheKey * 整理代码 * Update src/app/service/service_worker/permission_verify.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 75180b7 commit b34f3ea

3 files changed

Lines changed: 90 additions & 61 deletions

File tree

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

Lines changed: 63 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,6 @@ const generateUniqueMarkerID = () => {
6565
return `MARKER::${u1}${u2}`;
6666
};
6767

68-
const enum xhrExtraCode {
69-
INVALID_URL = 0x20,
70-
DOMAIN_NOT_INCLUDED = 0x30,
71-
DOMAIN_IN_BLACKLIST = 0x40,
72-
}
73-
7468
type OnBeforeSendHeadersOptions = `${chrome.webRequest.OnBeforeSendHeadersOptions}`;
7569
type OnHeadersReceivedOptions = `${chrome.webRequest.OnHeadersReceivedOptions}`;
7670

@@ -283,7 +277,7 @@ export default class GMApi {
283277
}
284278

285279
@PermissionVerify.API({
286-
confirm: async (request: GMApiRequest<[string, GMTypes.CookieDetails]>, sender: IGetSender) => {
280+
confirm: async (request: GMApiRequest<[string, GMTypes.CookieDetails]>, sender: IGetSender, gmApi: GMApi) => {
287281
if (request.params[0] === "store") {
288282
return true;
289283
}
@@ -299,6 +293,14 @@ export default class GMApi {
299293
url.hostname = detail.domain || "";
300294
}
301295
if (getConnectMatched(request.script.metadata.connect, url, sender) === ConnectMatch.NONE) {
296+
// 检查是否配置了权限
297+
const ret = await gmApi.permissionVerify.queryPermission(request, {
298+
permission: "cookie",
299+
permissionValue: url.host,
300+
});
301+
if (ret && ret.allow) {
302+
return true;
303+
}
302304
throw new Error("hostname must be in the definition of connect");
303305
}
304306
const metadata: { [key: string]: string } = {};
@@ -689,18 +691,37 @@ export default class GMApi {
689691
}
690692

691693
@PermissionVerify.API({
692-
confirm: async (request: GMApiRequest<[GMSend.XHRDetails]>, sender: IGetSender, GMApiInstance: GMApi) => {
693-
const config = <GMSend.XHRDetails>request.params[0];
694+
confirm: async (request: GMApiRequest<[GMSend.XHRDetails?]>, sender: IGetSender, GMApiInstance: GMApi) => {
695+
const msgConn = sender.getConnect();
696+
if (!msgConn) {
697+
throw new Error("GM_xmlhttpRequest ERROR: msgConn is undefined");
698+
}
699+
const throwErrorFn = (error: string) => {
700+
msgConn.sendMessage({
701+
action: "onerror",
702+
data: {
703+
status: 0,
704+
responseHeaders: "",
705+
error: error,
706+
readyState: 4, // ERROR. DONE.
707+
},
708+
});
709+
return new Error(error);
710+
};
711+
const details = request.params[0];
712+
if (!details) {
713+
throw throwErrorFn("param is failed");
714+
}
694715
let url;
695716
try {
696-
url = new URL(config.url);
717+
url = new URL(details.url);
697718
} catch {
698-
request.extraCode = xhrExtraCode.INVALID_URL;
699-
return false;
719+
const msg = `Refused to connect to "${details.url}": The url is invalid`;
720+
throw throwErrorFn(msg);
700721
}
701722
if (GMApiInstance.gmExternalDependencies.isBlacklistNetwork(url)) {
702-
request.extraCode = xhrExtraCode.DOMAIN_IN_BLACKLIST;
703-
return false;
723+
const msg = `Refused to connect to "${details.url}": URL is blacklisted`;
724+
throw throwErrorFn(msg);
704725
}
705726
const connectMatched = getConnectMatched(request.script.metadata.connect, url, sender);
706727
if (connectMatched === ConnectMatch.ALL) {
@@ -713,15 +734,24 @@ export default class GMApi {
713734
}
714735
// @connect 没有匹配,但有列明 @connect 的话,则自动拒绝
715736
if (request.script.metadata.connect?.find((e) => !!e)) {
716-
request.extraCode = xhrExtraCode.DOMAIN_NOT_INCLUDED;
717-
return false;
737+
// 查询数据库权限记录,如果之前用户允许过该域名,则放行,否则拒绝
738+
const ret = await GMApiInstance.permissionVerify.queryPermission(request, {
739+
permission: "cors",
740+
permissionValue: url.hostname,
741+
wildcard: true,
742+
});
743+
if (ret && ret.allow) {
744+
return true;
745+
}
746+
const msg = `Refused to connect to "${details.url}": This domain is not a part of the @connect list`;
747+
throw throwErrorFn(msg);
718748
}
719749
// 其他情况:要询问用户
720750
}
721751
const metadata: { [key: string]: string } = {};
722752
metadata[i18next.t("script_name")] = i18nName(request.script);
723753
metadata[i18next.t("request_domain")] = url.hostname;
724-
metadata[i18next.t("request_url")] = config.url;
754+
metadata[i18next.t("request_url")] = details.url;
725755

726756
return {
727757
permission: "cors",
@@ -735,14 +765,12 @@ export default class GMApi {
735765
},
736766
alias: ["GM.xmlHttpRequest"],
737767
})
738-
async GM_xmlhttpRequest(request: GMApiRequest<[GMSend.XHRDetails?]>, sender: IGetSender) {
768+
async GM_xmlhttpRequest(request: GMApiRequest<[GMSend.XHRDetails]>, sender: IGetSender) {
739769
if (!sender.isType(GetSenderType.CONNECT)) {
740770
throw new Error("GM_xmlhttpRequest ERROR: sender is not MessageConnect");
741771
}
742-
const msgConn = sender.getConnect();
743-
if (!msgConn) {
744-
throw new Error("GM_xmlhttpRequest ERROR: msgConn is undefined");
745-
}
772+
const msgConn = sender.getConnect()!;
773+
746774
let isConnDisconnected = false;
747775
msgConn.onDisconnect(() => {
748776
isConnDisconnected = true;
@@ -754,40 +782,8 @@ export default class GMApi {
754782

755783
const resultParam = new SWRequestResultParams(markerID);
756784

757-
const throwErrorFn = (error: string) => {
758-
if (!isConnDisconnected) {
759-
msgConn.sendMessage({
760-
action: "onerror",
761-
data: {
762-
status: resultParam.statusCode,
763-
responseHeaders: resultParam.responseHeaders,
764-
error: `${error}`,
765-
readyState: 4, // ERROR. DONE.
766-
},
767-
});
768-
}
769-
return new Error(`${error}`);
770-
};
771-
772785
const details = request.params[0];
773-
if (!details) {
774-
throw throwErrorFn("param is failed");
775-
}
776786

777-
if (request.extraCode === xhrExtraCode.INVALID_URL) {
778-
const msg = `Refused to connect to "${details.url}": The url is invalid`;
779-
throw throwErrorFn(msg);
780-
}
781-
if (request.extraCode === xhrExtraCode.DOMAIN_NOT_INCLUDED) {
782-
// 'Refused to connect to "https://nonexistent-domain-abcxyz.test/": This domain is not a part of the @connect list'
783-
const msg = `Refused to connect to "${details.url}": This domain is not a part of the @connect list`;
784-
throw throwErrorFn(msg);
785-
}
786-
if (request.extraCode === xhrExtraCode.DOMAIN_IN_BLACKLIST) {
787-
// 'Refused to connect to "https://example.org/": URL is blacklisted'
788-
const msg = `Refused to connect to "${details.url}": URL is blacklisted`;
789-
throw throwErrorFn(msg);
790-
}
791787
try {
792788
/*
793789
There are TM-specific parameters:
@@ -879,7 +875,19 @@ export default class GMApi {
879875
msgConn.onDisconnect(offscreenCon.disconnect.bind(offscreenCon));
880876
}
881877
} catch (e: any) {
882-
throw throwErrorFn(`GM_xmlhttpRequest ERROR: ${e?.message || e || "Unknown Error"}`);
878+
const errorMsg = `GM_xmlhttpRequest ERROR: ${e?.message || e || "Unknown Error"}`;
879+
if (!isConnDisconnected) {
880+
msgConn.sendMessage({
881+
action: "onerror",
882+
data: {
883+
status: resultParam.statusCode,
884+
responseHeaders: resultParam.responseHeaders,
885+
error: errorMsg,
886+
readyState: 4, // ERROR. DONE.
887+
},
888+
});
889+
}
890+
throw new Error(errorMsg);
883891
}
884892
}
885893

src/app/service/service_worker/permission_verify.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -186,13 +186,27 @@ export default class PermissionVerify {
186186
});
187187
}
188188

189-
async confirm<T>(request: GMApiRequest<T>, confirm: boolean | ConfirmParam, sender: IGetSender): Promise<boolean> {
190-
if (typeof confirm === "boolean") {
191-
return confirm;
189+
buildCacheKey(
190+
request: GMApiRequest,
191+
confirm: {
192+
permission: string;
193+
permissionValue?: string;
194+
}
195+
) {
196+
return `${CACHE_KEY_PERMISSION}${request.script.uuid}:${confirm.permission}:${confirm.permissionValue || ""}`;
197+
}
198+
199+
async queryPermission<T>(
200+
request: GMApiRequest<T>,
201+
confirm: {
202+
permission: string;
203+
permissionValue?: string;
204+
wildcard?: boolean;
192205
}
193-
const cacheKey = `${CACHE_KEY_PERMISSION}${request.script.uuid}:${confirm.permission}:${confirm.permissionValue || ""}`;
206+
): Promise<Permission | undefined> {
207+
const cacheKey = this.buildCacheKey(request, confirm);
194208
// 从数据库中查询是否有此权限
195-
const ret = await cacheInstance.getOrSet(cacheKey, async () => {
209+
return await cacheInstance.getOrSet(cacheKey, async () => {
196210
let model = await this.permissionDAO.findByKey(request.uuid, confirm.permission, confirm.permissionValue || "");
197211
if (!model) {
198212
// 允许通配
@@ -202,6 +216,13 @@ export default class PermissionVerify {
202216
}
203217
return model;
204218
});
219+
}
220+
221+
async confirm<T>(request: GMApiRequest<T>, confirm: boolean | ConfirmParam, sender: IGetSender): Promise<boolean> {
222+
if (typeof confirm === "boolean") {
223+
return confirm;
224+
}
225+
const ret = await this.queryPermission(request, confirm);
205226
// 有查询到结果,进入判断,不再需要用户确认
206227
if (ret) {
207228
if (ret.allow) {
@@ -238,6 +259,7 @@ export default class PermissionVerify {
238259
}
239260
// 临时 放入缓存
240261
if (userConfirm.type >= 2) {
262+
const cacheKey = this.buildCacheKey(request, confirm);
241263
cacheInstance.set(cacheKey, model);
242264
}
243265
// 总是 放入数据库

src/app/service/service_worker/types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ export type MessageRequest<T = any[]> = {
5050

5151
export type GMApiRequest<T = any> = MessageRequest<T> & {
5252
script: Script;
53-
extraCode?: number; // 用于 confirm 传额外资讯
5453
};
5554

5655
export type NotificationMessageOption = {

0 commit comments

Comments
 (0)