summaryrefslogtreecommitdiff
path: root/packages/backend/src/core
diff options
context:
space:
mode:
authorrectcoordsystem <heohyun73@gmail.com>2024-11-06 05:31:11 +0900
committerJulia Johannesen <julia@insertdomain.name>2024-11-20 19:17:25 -0500
commitf36f4b5398561dcf7365729c530f5b1868c6b994 (patch)
tree6ec5c6abd7794318caed54e43caa72e887386848 /packages/backend/src/core
parentfix: Try using `CacheService` to avoid excess db lookups (diff)
downloadsharkey-f36f4b5398561dcf7365729c530f5b1868c6b994.tar.gz
sharkey-f36f4b5398561dcf7365729c530f5b1868c6b994.tar.bz2
sharkey-f36f4b5398561dcf7365729c530f5b1868c6b994.zip
fix(backend): check target IP before sending HTTP request
Diffstat (limited to 'packages/backend/src/core')
-rw-r--r--packages/backend/src/core/DownloadService.ts22
-rw-r--r--packages/backend/src/core/HttpRequestService.ts90
2 files changed, 88 insertions, 24 deletions
diff --git a/packages/backend/src/core/DownloadService.ts b/packages/backend/src/core/DownloadService.ts
index 0e992f05de..05b9e64a37 100644
--- a/packages/backend/src/core/DownloadService.ts
+++ b/packages/backend/src/core/DownloadService.ts
@@ -6,7 +6,6 @@
import * as fs from 'node:fs';
import * as stream from 'node:stream/promises';
import { Inject, Injectable } from '@nestjs/common';
-import ipaddr from 'ipaddr.js';
import chalk from 'chalk';
import got, * as Got from 'got';
import { parse } from 'content-disposition';
@@ -70,13 +69,6 @@ export class DownloadService {
},
enableUnixSockets: false,
}).on('response', (res: Got.Response) => {
- if ((process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'test') && !this.config.proxy && res.ip) {
- if (this.isPrivateIp(res.ip)) {
- this.logger.warn(`Blocked address: ${res.ip}`);
- req.destroy();
- }
- }
-
const contentLength = res.headers['content-length'];
if (contentLength != null) {
const size = Number(contentLength);
@@ -139,18 +131,4 @@ export class DownloadService {
cleanup();
}
}
-
- @bindThis
- private isPrivateIp(ip: string): boolean {
- const parsedIp = ipaddr.parse(ip);
-
- for (const net of this.config.allowedPrivateNetworks ?? []) {
- const cidr = ipaddr.parseCIDR(net);
- if (cidr[0].kind() === parsedIp.kind() && parsedIp.match(ipaddr.parseCIDR(net))) {
- return false;
- }
- }
-
- return parsedIp.range() !== 'unicast';
- }
}
diff --git a/packages/backend/src/core/HttpRequestService.ts b/packages/backend/src/core/HttpRequestService.ts
index 08e9f46b2d..6c013eacc0 100644
--- a/packages/backend/src/core/HttpRequestService.ts
+++ b/packages/backend/src/core/HttpRequestService.ts
@@ -6,6 +6,7 @@
import * as http from 'node:http';
import * as https from 'node:https';
import * as net from 'node:net';
+import ipaddr from 'ipaddr.js';
import CacheableLookup from 'cacheable-lookup';
import fetch from 'node-fetch';
import { HttpProxyAgent, HttpsProxyAgent } from 'hpagent';
@@ -26,6 +27,91 @@ export type HttpRequestSendOptions = {
};
@Injectable()
+class HttpRequestServiceAgent extends http.Agent {
+ constructor(
+ @Inject(DI.config)
+ private config: Config,
+
+ options?: Object
+ ) {
+ super(options);
+ }
+
+ @bindThis
+ public createConnection(options: Object, callback?: Function): net.Socket {
+ const socket = super.createConnection(options, callback)
+ .on('connect', ()=>{
+ const address = socket.remoteAddress;
+ if (process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'test') {
+ if (address && ipaddr.isValid(address)) {
+ if (this.isPrivateIp(address)) {
+ socket.destroy(new Error(`Blocked address: ${address}`));
+ }
+ }
+ }
+ });
+ return socket;
+ };
+
+ @bindThis
+ private isPrivateIp(ip: string): boolean {
+ const parsedIp = ipaddr.parse(ip);
+
+ for (const net of this.config.allowedPrivateNetworks ?? []) {
+ const cidr = ipaddr.parseCIDR(net);
+ if (cidr[0].kind() === parsedIp.kind() && parsedIp.match(ipaddr.parseCIDR(net))) {
+ return false;
+ }
+ }
+
+ return parsedIp.range() !== 'unicast';
+ }
+}
+
+@Injectable()
+class HttpsRequestServiceAgent extends https.Agent {
+ constructor(
+ @Inject(DI.config)
+ private config: Config,
+
+ options?: Object
+ ) {
+ super(options);
+ }
+
+ @bindThis
+ public createConnection(options: Object, callback?: Function): net.Socket {
+ const socket = super.createConnection(options, callback)
+ .on('connect', ()=>{
+ const address = socket.remoteAddress;
+ if (process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'test') {
+ if (address && ipaddr.isValid(address)) {
+ if (this.isPrivateIp(address)) {
+ socket.destroy(new Error(`Blocked address: ${address}`));
+ }
+ }
+ }
+ });
+ return socket;
+ };
+
+ @bindThis
+ private isPrivateIp(ip: string): boolean {
+ const parsedIp = ipaddr.parse(ip);
+
+ for (const net of this.config.allowedPrivateNetworks ?? []) {
+ const cidr = ipaddr.parseCIDR(net);
+ if (cidr[0].kind() === parsedIp.kind() && parsedIp.match(ipaddr.parseCIDR(net))) {
+ return false;
+ }
+ }
+
+ return parsedIp.range() !== 'unicast';
+ }
+}
+
+
+@Injectable()
export class HttpRequestService {
/**
* Get http non-proxy agent
@@ -57,14 +143,14 @@ export class HttpRequestService {
lookup: false, // nativeのdns.lookupにfallbackしない
});
- this.http = new http.Agent({
+ this.http = new HttpRequestServiceAgent(config, {
keepAlive: true,
keepAliveMsecs: 30 * 1000,
lookup: cache.lookup as unknown as net.LookupFunction,
localAddress: config.outgoingAddress,
});
- this.https = new https.Agent({
+ this.https = new HttpsRequestServiceAgent(config, {
keepAlive: true,
keepAliveMsecs: 30 * 1000,
lookup: cache.lookup as unknown as net.LookupFunction,