Bugfix: disallow proxying of local resources

This commit is contained in:
DarkCat09 2023-08-17 18:00:39 +04:00
parent 232bde1cd8
commit d498488875
No known key found for this signature in database
GPG Key ID: 4785B6FB1C50A540
6 changed files with 81 additions and 18 deletions

View File

@ -1,15 +1,18 @@
<h1 align="center">
<a href="https://github.com/TxtDot/txtdot"><img src="https://github.com/TxtDot/.github/blob/main/imgs/TXTDot%20gh.png?raw=true" alt="txt." width="200"></a> <br>
<img alt="GitHub" src="https://img.shields.io/github/license/txtdot/txtdot">
<img alt="GitHub release (with filter)" src="https://img.shields.io/github/v/release/TxtDot/txtdot?display_name=release">
<a href="https://matrix.to/#/#txtdot:matrix.org"><img alt="Static Badge" src="https://img.shields.io/badge/matrix_chat-green">
</a>
<a href="https://github.com/TxtDot/txtdot"><img src="https://github.com/TxtDot/.github/blob/main/imgs/TXTDot%20gh.png?raw=true" alt="txt." width="200"></a>
<br>
<a href="https://github.com/TxtDot/txtdot/blob/main/LICENSE"><img alt="MIT license" src="https://img.shields.io/github/license/txtdot/txtdot?color=blue"></a>
<a href="https://github.com/TxtDot/txtdot/releases/latest"><img alt="Latest release" src="https://img.shields.io/github/v/release/TxtDot/txtdot?display_name=release"></a>
<a href="https://matrix.to/#/#txtdot:matrix.org"><img alt="Matrix chat" src="https://img.shields.io/badge/chat-matrix-blue"></a>
</h1>
HTTP proxy that parses only text, links and pictures from pages
reducing internet traffic, removing ads and heavy scripts.
Uses [Mozilla's readability.js](https://github.com/mozilla/readability),
[JSDOM](https://github.com/jsdom/jsdom),
[Fastify web framework](https://github.com/fastify/fastify).
## Installation
```bash
@ -30,7 +33,3 @@ npm run dev
npm run build
npm run start
```
Uses [Mozilla's readability.js](https://github.com/mozilla/readability),
[JSDOM](https://github.com/jsdom/jsdom),
[Fastify web framework](https://github.com/fastify/fastify).

9
package-lock.json generated
View File

@ -18,6 +18,7 @@
"dotenv": "^16.3.1",
"ejs": "^3.1.9",
"fastify": "^4.21.0",
"ip-range-check": "^0.2.0",
"jsdom": "^22.1.0"
},
"devDependencies": {
@ -1968,6 +1969,14 @@
"version": "2.0.4",
"license": "ISC"
},
"node_modules/ip-range-check": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/ip-range-check/-/ip-range-check-0.2.0.tgz",
"integrity": "sha512-oaM3l/3gHbLlt/tCWLvt0mj1qUaI+STuRFnUvARGCujK9vvU61+2JsDpmkMzR4VsJhuFXWWgeKKVnwwoFfzCqw==",
"dependencies": {
"ipaddr.js": "^1.0.1"
}
},
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",

View File

@ -14,6 +14,7 @@
"dotenv": "^16.3.1",
"ejs": "^3.1.9",
"fastify": "^4.21.0",
"ip-range-check": "^0.2.0",
"jsdom": "^22.1.0"
},
"devDependencies": {

View File

@ -1,5 +1,6 @@
export class EngineParseError extends Error {}
export class InvalidParameterError extends Error {}
export class LocalResourceError extends Error {}
export class NotHtmlMimetypeError extends Error {
url: string;
constructor(params: { url: string }) {

View File

@ -6,15 +6,24 @@ import { DOMWindow } from "jsdom";
import readability from "./readability";
import google from "./google";
import { generateProxyUrl } from "../utils";
import { InvalidParameterError, NotHtmlMimetypeError } from "../errors/main";
import { generateProxyUrl } from "../utils";
import isLocalResource from "../islocal";
import { InvalidParameterError, LocalResourceError, NotHtmlMimetypeError } from "../errors/main";
export default async function handlePage(
url: string,
requestUrl: URL,
url: string, // remote URL
requestUrl: URL, // proxy URL
engine?: string
): Promise<IHandlerOutput> {
const urlObj = new URL(url);
if (await isLocalResource(urlObj)) {
throw new LocalResourceError();
}
if (engine && engineList.indexOf(engine) === -1) {
throw new InvalidParameterError("Invalid engine");
}
@ -26,7 +35,7 @@ export default async function handlePage(
throw new NotHtmlMimetypeError({ url });
}
const window = new JSDOM(response.data, { url: url }).window;
const window = new JSDOM(response.data, { url }).window;
[...window.document.getElementsByTagName("a")].forEach((link) => {
link.href = generateProxyUrl(requestUrl, link.href, engine);
@ -36,9 +45,7 @@ export default async function handlePage(
return engines[engine](window);
}
const host = new URL(url).hostname;
return fallback[host]?.(window) || fallback["*"](window);
return fallback[urlObj.host]?.(window) || fallback["*"](window);
}
interface Engines {

46
src/islocal.ts Normal file
View File

@ -0,0 +1,46 @@
import dns from "dns";
import ipRangeCheck from "ip-range-check";
const subnets = [
"0.0.0.0/8",
"127.0.0.0/8",
"10.0.0.0/8",
"100.64.0.0/10",
"169.254.0.0/16",
"172.16.0.0/12",
"192.0.0.0/24",
"192.0.2.0/24",
"192.88.99.0/24",
"192.168.0.0/16",
"198.18.0.0/15",
"198.51.100.0/24",
"203.0.113.0/24",
"224.0.0.0/4",
"233.252.0.0/24",
"240.0.0.0/4",
"255.255.255.255/32",
"::/128",
"::1/128",
"::ffff:0:0/96",
"::ffff:0:0:0/96",
"64:ff9b::/96",
"64:ff9b:1::/48",
"100::/64",
"2001:0000::/32",
"2001:20::/28",
"2001:db8::/32",
"2002::/16",
"fc00::/7",
"fe80::/64",
"ff00::/8",
];
export default async function isLocalResource(url: URL): Promise<boolean> {
// Resolve domain name
const addr = (
await dns.promises.lookup(url.hostname)
).address;
// Check if IP is in local network
return ipRangeCheck(addr, subnets);
}