UserScript: add csp bypassing fetch (#284)
This commit is contained in:
parent
53d0a55561
commit
8a5a5c7d1e
3 changed files with 114 additions and 2 deletions
107
browser/GMPolyfill.js
Normal file
107
browser/GMPolyfill.js
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2022 Vendicated and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function fetchOptions(url) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const opt = {
|
||||||
|
method: "OPTIONS",
|
||||||
|
url: url,
|
||||||
|
};
|
||||||
|
opt.onload = resp => resolve(resp.responseHeaders);
|
||||||
|
opt.ontimeout = () => reject("fetch timeout");
|
||||||
|
opt.onerror = () => reject("fetch error");
|
||||||
|
opt.onabort = () => reject("fetch abort");
|
||||||
|
GM_xmlhttpRequest(opt);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseHeaders(headers) {
|
||||||
|
if (!headers)
|
||||||
|
return {};
|
||||||
|
const result = {};
|
||||||
|
const headersArr = headers.trim().split("\n");
|
||||||
|
for (var i = 0; i < headersArr.length; i++) {
|
||||||
|
var row = headersArr[i];
|
||||||
|
var index = row.indexOf(":")
|
||||||
|
, key = row.slice(0, index).trim().toLowerCase()
|
||||||
|
, value = row.slice(index + 1).trim();
|
||||||
|
|
||||||
|
if (result[key] === undefined) {
|
||||||
|
result[key] = value;
|
||||||
|
} else if (Array.isArray(result[key])) {
|
||||||
|
result[key].push(value);
|
||||||
|
} else {
|
||||||
|
result[key] = [result[key], value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns true if CORS permits request
|
||||||
|
async function checkCors(url, method) {
|
||||||
|
const headers = parseHeaders(await fetchOptions(url));
|
||||||
|
|
||||||
|
const origin = headers["access-control-allow-origin"];
|
||||||
|
if (origin !== "*" && origin !== window.location.origin) return false;
|
||||||
|
|
||||||
|
const methods = headers["access-control-allow-methods"]?.split(/,\s/g);
|
||||||
|
if (methods && !methods.includes(method)) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function blobTo(to, blob) {
|
||||||
|
if (to === "arrayBuffer" && blob.arrayBuffer) return blob.arrayBuffer();
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
var fileReader = new FileReader();
|
||||||
|
fileReader.onload = event => resolve(event.target.result);
|
||||||
|
if (to === "arrayBuffer") fileReader.readAsArrayBuffer(blob);
|
||||||
|
else if (to === "text") fileReader.readAsText(blob, "utf-8");
|
||||||
|
else reject("unknown to");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function GM_fetch(url, opt) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
checkCors(url, opt?.method || "GET")
|
||||||
|
.then(can => {
|
||||||
|
if (can) {
|
||||||
|
// https://www.tampermonkey.net/documentation.php?ext=dhdg#GM_xmlhttpRequest
|
||||||
|
const options = opt || {};
|
||||||
|
options.url = url;
|
||||||
|
options.data = options.body;
|
||||||
|
options.responseType = "blob";
|
||||||
|
options.onload = resp => {
|
||||||
|
var blob = resp.response;
|
||||||
|
resp.blob = () => Promise.resolve(blob);
|
||||||
|
resp.arrayBuffer = () => blobTo("arrayBuffer", blob);
|
||||||
|
resp.text = () => blobTo("text", blob);
|
||||||
|
resp.json = async () => JSON.parse(await blobTo("text", blob));
|
||||||
|
resolve(resp);
|
||||||
|
};
|
||||||
|
options.ontimeout = () => reject("fetch timeout");
|
||||||
|
options.onerror = () => reject("fetch error");
|
||||||
|
options.onabort = () => reject("fetch abort");
|
||||||
|
GM_xmlhttpRequest(options);
|
||||||
|
} else {
|
||||||
|
reject("CORS issue");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
export const fetch = GM_fetch;
|
|
@ -7,7 +7,7 @@
|
||||||
// @supportURL https://github.com/Vendicated/Vencord
|
// @supportURL https://github.com/Vendicated/Vencord
|
||||||
// @license GPL-3.0
|
// @license GPL-3.0
|
||||||
// @match *://*.discord.com/*
|
// @match *://*.discord.com/*
|
||||||
// @grant none
|
// @grant GM_xmlhttpRequest
|
||||||
// @run-at document-start
|
// @run-at document-start
|
||||||
// @compatible chrome Chrome + Tampermonkey or Violentmonkey
|
// @compatible chrome Chrome + Tampermonkey or Violentmonkey
|
||||||
// @compatible firefox Firefox Tampermonkey
|
// @compatible firefox Firefox Tampermonkey
|
||||||
|
|
|
@ -60,13 +60,18 @@ await Promise.all(
|
||||||
}),
|
}),
|
||||||
esbuild.build({
|
esbuild.build({
|
||||||
...commonOptions,
|
...commonOptions,
|
||||||
|
inject: ["browser/GMPolyfill.js", ...(commonOptions?.inject || [])],
|
||||||
|
define: {
|
||||||
|
"window": "unsafeWindow",
|
||||||
|
...(commonOptions?.define)
|
||||||
|
},
|
||||||
outfile: "dist/Vencord.user.js",
|
outfile: "dist/Vencord.user.js",
|
||||||
banner: {
|
banner: {
|
||||||
js: readFileSync("browser/userscript.meta.js", "utf-8").replace("%version%", `${PackageJSON.version}.${new Date().getTime()}`)
|
js: readFileSync("browser/userscript.meta.js", "utf-8").replace("%version%", `${PackageJSON.version}.${new Date().getTime()}`)
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
// UserScripts get wrapped in an iife, so define Vencord prop on window that returns our local
|
// UserScripts get wrapped in an iife, so define Vencord prop on window that returns our local
|
||||||
js: "Object.defineProperty(window,'Vencord',{get:()=>Vencord});"
|
js: "Object.defineProperty(unsafeWindow,'Vencord',{get:()=>Vencord});"
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in a new issue