Bugfix: disallow proxying of local resources
This commit is contained in:
parent
232bde1cd8
commit
d498488875
19
README.md
19
README.md
@ -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
9
package-lock.json
generated
@ -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",
|
||||
|
@ -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": {
|
||||
|
@ -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 }) {
|
||||
|
@ -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
46
src/islocal.ts
Normal 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);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user