MCPcopy
hub / github.com/MagicMirrorOrg/MagicMirror / cors

Function cors

js/server_functions.js:57–145  ·  view source on GitHub ↗

* A method that forwards HTTP Get-methods to the internet to avoid CORS-errors. * * Example input request url: /cors?sendheaders=header1:value1,header2:value2&expectedheaders=header1,header2&url=http://www.test.com/path?param1=value1 * * Only the url-param of the input request url is required. I

(req, res)

Source from the content-addressed store, hash-verified

55 * @returns {Promise<void>} A promise that resolves when the response is sent
56 */
57async function cors (req, res) {
58 if (global.config.cors === "disabled") {
59 Log.error("CORS is disabled, you need to enable it in `config.js` by setting `cors` to `allowAll` or `allowWhitelist`");
60 return res.status(403).json({ error: "CORS proxy is disabled" });
61 }
62 let url;
63 try {
64 const urlRegEx = "url=(.+?)$";
65
66 const match = new RegExp(urlRegEx, "g").exec(req.url);
67 if (!match) {
68 url = `invalid url: ${req.url}`;
69 Log.error(url);
70 return res.status(400).send(url);
71 } else {
72 url = match[1];
73 if (typeof global.config !== "undefined") {
74 if (config.hideConfigSecrets) {
75 url = replaceSecretPlaceholder(url);
76 }
77 }
78
79 // Validate protocol before attempting connection (non-http/https are never allowed)
80 let parsed;
81 try {
82 parsed = new URL(url);
83 } catch {
84 Log.warn(`SSRF blocked (invalid URL): ${url}`);
85 return res.status(403).json({ error: "Forbidden: private or reserved addresses are not allowed" });
86 }
87 if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
88 Log.warn(`SSRF blocked (protocol): ${url}`);
89 return res.status(403).json({ error: "Forbidden: private or reserved addresses are not allowed" });
90 }
91
92 // Block localhost by hostname before even creating the dispatcher (no DNS needed).
93 if (parsed.hostname.toLowerCase() === "localhost") {
94 Log.warn(`SSRF blocked (localhost): ${url}`);
95 return res.status(403).json({ error: "Forbidden: private or reserved addresses are not allowed" });
96 }
97
98 // Whitelist check: if enabled, only allow explicitly listed domains
99 if (global.config.cors === "allowWhitelist" && !global.config.corsDomainWhitelist.includes(parsed.hostname.toLowerCase())) {
100 Log.warn(`CORS blocked (not in whitelist): ${url}`);
101 return res.status(403).json({ error: "Forbidden: domain not in corsDomainWhitelist" });
102 }
103
104 const headersToSend = getHeadersToSend(req.url);
105 const expectedReceivedHeaders = geExpectedReceivedHeaders(req.url);
106 Log.log(`cors url: ${url}`);
107
108 // Resolve DNS once and validate the IP. The validated IP is then pinned
109 // for the actual connection so fetch() cannot re-resolve to a different
110 // address. This prevents DNS rebinding / TOCTOU attacks (GHSA-xhvw-r95j-xm4v).
111 const { address, family } = await dns.promises.lookup(parsed.hostname);
112 if (ipaddr.process(address).range() !== "unicast") {
113 Log.warn(`SSRF blocked: ${url}`);
114 return res.status(403).json({ error: "Forbidden: private or reserved addresses are not allowed" });

Callers 2

ServerFunction · 0.85

Calls 4

replaceSecretPlaceholderFunction · 0.85
getHeadersToSendFunction · 0.85
fetchMethod · 0.80

Tested by

no test coverage detected