From 9dad70b2e4a67c017c504cefbdafeaad83926a73 Mon Sep 17 00:00:00 2001 From: Lea Date: Mon, 19 Dec 2022 18:22:43 +0100 Subject: [PATCH] fix: fixed several issues with proxy - proxy no longer crashes with malformed input - use URL whitelist instead of blindly proxying everything - clean up code --- p/server.js | 71 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 49 insertions(+), 22 deletions(-) diff --git a/p/server.js b/p/server.js index a720225..8d385b2 100644 --- a/p/server.js +++ b/p/server.js @@ -2,48 +2,75 @@ const fs = require("fs"); const express = require("express"); const fetch = require("node-fetch"); const htmlParser = require("node-html-parser"); +const { URL } = require("url"); + +// Array of hostnames that will be proxied +const URL_WHITELIST = [ + 'i.ytimg.com', + 'yt3.googleusercontent.com', + 'cdn.glitch.global', + 'cdn.statically.io', + 'site-assets.fontawesome.com', + 'fonts.gstatic.com', + 'yt3.ggpht.com', + 'tube.kuylar.dev', + 'lh3.googleusercontent.com', + 'is4-ssl.mzstatic.com', + 'twemoji.maxcdn.com', + 'unpkg.com', +]; const app = express(); app.use(express.json()); // for parsing application/json app.use(express.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded +app.use(function (req, res, next) { + console.log(`=> ${req.method} ${req.originalUrl.slice(1)}`) + next(); +}); + app.use(function (req, res, next) { res.header("Access-Control-Allow-Origin", "*"); next(); }); -let Proxy = async (req, res) => { - const url = "https://" + req.originalUrl.slice(10); - let f = await fetch(url, { - method: req.method, - }); - if (false && f.headers.get("content-type").includes("html")) { - const body = await f.text(); - if (false && !htmlParser.valid(body)) { - console.warn(`[ERROR] Invalid HTML at ${url}`); - f.body.pipe(res); - return; +/** + * @param {express.Request} req + * @param {express.Response} res + */ +const proxy = async (req, res) => { + try { + let url; + + try { + url = new URL("https://" + req.originalUrl.slice(1)); + } catch(e) { + console.log('==> Cannot parse URL: ' + e); + return res.status(400).send('Malformed URL'); } - const root = htmlParser.parse(body); - let html = root.childNodes.filter( - (x) => x.tagName && x.tagName.toLowerCase() == "html" - )[0]; - if (!html) { - console.warn(`[ERROR] No at ${url}`); - res.send(body); + if (!URL_WHITELIST.includes(url.host)) { + console.log(`==> Refusing to proxy host ${url.host}`); + res.status(401).send(`Hostname '${url.host}' is not permitted`); return; } - res.send(html.toString()); - } else { + console.log(`==> Proxying request`); + + let f = await fetch(url, { + method: req.method, + }); + f.body.pipe(res); + } catch(e) { + console.log(`==> Error: ${e}`); + res.status(500).send('Internal server error'); } }; const listener = (req, res) => { - Proxy(req, res); + proxy(req, res); }; app.get("/", (req, res) => @@ -52,4 +79,4 @@ app.get("/", (req, res) => app.all("/*", listener); -app.listen(3000, () => {}); +app.listen(3000, () => console.log('Listening on 0.0.0.0:3000'));