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">
|
<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>
|
<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>
|
||||||
<img alt="GitHub" src="https://img.shields.io/github/license/txtdot/txtdot">
|
<br>
|
||||||
<img alt="GitHub release (with filter)" src="https://img.shields.io/github/v/release/TxtDot/txtdot?display_name=release">
|
<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://matrix.to/#/#txtdot:matrix.org"><img alt="Static Badge" src="https://img.shields.io/badge/matrix_chat-green">
|
<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>
|
<a href="https://matrix.to/#/#txtdot:matrix.org"><img alt="Matrix chat" src="https://img.shields.io/badge/chat-matrix-blue"></a>
|
||||||
|
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
HTTP proxy that parses only text, links and pictures from pages
|
HTTP proxy that parses only text, links and pictures from pages
|
||||||
reducing internet traffic, removing ads and heavy scripts.
|
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
|
## Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@ -30,7 +33,3 @@ npm run dev
|
|||||||
npm run build
|
npm run build
|
||||||
npm run start
|
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",
|
"dotenv": "^16.3.1",
|
||||||
"ejs": "^3.1.9",
|
"ejs": "^3.1.9",
|
||||||
"fastify": "^4.21.0",
|
"fastify": "^4.21.0",
|
||||||
|
"ip-range-check": "^0.2.0",
|
||||||
"jsdom": "^22.1.0"
|
"jsdom": "^22.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -1968,6 +1969,14 @@
|
|||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"license": "ISC"
|
"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": {
|
"node_modules/ipaddr.js": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"ejs": "^3.1.9",
|
"ejs": "^3.1.9",
|
||||||
"fastify": "^4.21.0",
|
"fastify": "^4.21.0",
|
||||||
|
"ip-range-check": "^0.2.0",
|
||||||
"jsdom": "^22.1.0"
|
"jsdom": "^22.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
export class EngineParseError extends Error {}
|
export class EngineParseError extends Error {}
|
||||||
export class InvalidParameterError extends Error {}
|
export class InvalidParameterError extends Error {}
|
||||||
|
export class LocalResourceError extends Error {}
|
||||||
export class NotHtmlMimetypeError extends Error {
|
export class NotHtmlMimetypeError extends Error {
|
||||||
url: string;
|
url: string;
|
||||||
constructor(params: { url: string }) {
|
constructor(params: { url: string }) {
|
||||||
|
@ -6,15 +6,24 @@ import { DOMWindow } from "jsdom";
|
|||||||
|
|
||||||
import readability from "./readability";
|
import readability from "./readability";
|
||||||
import google from "./google";
|
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(
|
export default async function handlePage(
|
||||||
url: string,
|
url: string, // remote URL
|
||||||
requestUrl: URL,
|
requestUrl: URL, // proxy URL
|
||||||
engine?: string
|
engine?: string
|
||||||
): Promise<IHandlerOutput> {
|
): Promise<IHandlerOutput> {
|
||||||
|
|
||||||
|
const urlObj = new URL(url);
|
||||||
|
|
||||||
|
if (await isLocalResource(urlObj)) {
|
||||||
|
throw new LocalResourceError();
|
||||||
|
}
|
||||||
|
|
||||||
if (engine && engineList.indexOf(engine) === -1) {
|
if (engine && engineList.indexOf(engine) === -1) {
|
||||||
throw new InvalidParameterError("Invalid engine");
|
throw new InvalidParameterError("Invalid engine");
|
||||||
}
|
}
|
||||||
@ -26,7 +35,7 @@ export default async function handlePage(
|
|||||||
throw new NotHtmlMimetypeError({ url });
|
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) => {
|
[...window.document.getElementsByTagName("a")].forEach((link) => {
|
||||||
link.href = generateProxyUrl(requestUrl, link.href, engine);
|
link.href = generateProxyUrl(requestUrl, link.href, engine);
|
||||||
@ -36,9 +45,7 @@ export default async function handlePage(
|
|||||||
return engines[engine](window);
|
return engines[engine](window);
|
||||||
}
|
}
|
||||||
|
|
||||||
const host = new URL(url).hostname;
|
return fallback[urlObj.host]?.(window) || fallback["*"](window);
|
||||||
|
|
||||||
return fallback[host]?.(window) || fallback["*"](window);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Engines {
|
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