summaryrefslogtreecommitdiff
path: root/packages/backend/src/core
diff options
context:
space:
mode:
authorHazelnoot <acomputerdog@gmail.com>2025-07-08 11:27:31 -0400
committerdakkar <dakkar@thenautilus.net>2025-07-27 19:39:20 +0100
commit3c59a7ae01f7ea7094178cf961c3f4a668672f08 (patch)
treef0310bd53666b20223339c3fd269defa655611a3 /packages/backend/src/core
parentrefactor actor validation to reduce code duplication (diff)
downloadsharkey-3c59a7ae01f7ea7094178cf961c3f4a668672f08.tar.gz
sharkey-3c59a7ae01f7ea7094178cf961c3f4a668672f08.tar.bz2
sharkey-3c59a7ae01f7ea7094178cf961c3f4a668672f08.zip
allow HTTP connections to private IPs
Diffstat (limited to 'packages/backend/src/core')
-rw-r--r--packages/backend/src/core/HttpRequestService.ts17
-rw-r--r--packages/backend/src/core/UtilityService.ts8
2 files changed, 18 insertions, 7 deletions
diff --git a/packages/backend/src/core/HttpRequestService.ts b/packages/backend/src/core/HttpRequestService.ts
index 046b0dc244..bbc127fa86 100644
--- a/packages/backend/src/core/HttpRequestService.ts
+++ b/packages/backend/src/core/HttpRequestService.ts
@@ -20,7 +20,6 @@ import type { IObject, IObjectWithId } from '@/core/activitypub/type.js';
import { UtilityService } from '@/core/UtilityService.js';
import { ApUtilityService } from '@/core/activitypub/ApUtilityService.js';
import type { Response } from 'node-fetch';
-import type { URL } from 'node:url';
import type { Socket } from 'node:net';
export type HttpRequestSendOptions = {
@@ -28,6 +27,15 @@ export type HttpRequestSendOptions = {
validators?: ((res: Response) => void)[];
};
+export function isPrivateUrl(url: URL): boolean {
+ if (!ipaddr.isValid(url.hostname)) {
+ return false;
+ }
+
+ const ip = ipaddr.parse(url.hostname);
+ return ip.range() !== 'unicast';
+}
+
export function isPrivateIp(allowedPrivateNetworks: PrivateNetwork[] | undefined, ip: string, port?: number): boolean {
const parsedIp = ipaddr.parse(ip);
@@ -303,6 +311,7 @@ export class HttpRequestService {
timeout?: number,
size?: number,
isLocalAddressAllowed?: boolean,
+ allowHttp?: boolean,
} = {},
extra: HttpRequestSendOptions = {
throwErrorWhenResponseNotOk: true,
@@ -311,7 +320,9 @@ export class HttpRequestService {
): Promise<Response> {
const timeout = args.timeout ?? 5000;
- this.utilityService.assertUrl(url);
+ const parsedUrl = new URL(url);
+ const allowHttp = args.allowHttp || isPrivateUrl(parsedUrl);
+ this.utilityService.assertUrl(parsedUrl, allowHttp);
const controller = new AbortController();
setTimeout(() => {
@@ -320,7 +331,7 @@ export class HttpRequestService {
const isLocalAddressAllowed = args.isLocalAddressAllowed ?? false;
- const res = await fetch(url, {
+ const res = await fetch(parsedUrl, {
method: args.method ?? 'GET',
headers: {
'User-Agent': this.config.userAgent,
diff --git a/packages/backend/src/core/UtilityService.ts b/packages/backend/src/core/UtilityService.ts
index 27d3cb7776..a90774cf59 100644
--- a/packages/backend/src/core/UtilityService.ts
+++ b/packages/backend/src/core/UtilityService.ts
@@ -226,7 +226,7 @@ export class UtilityService {
* @throws {IdentifiableError} If URL contains credentials
*/
@bindThis
- public assertUrl(url: string | URL): URL | never {
+ public assertUrl(url: string | URL, allowHttp?: boolean): URL | never {
// If string, parse and validate
if (typeof(url) === 'string') {
try {
@@ -237,7 +237,7 @@ export class UtilityService {
}
// Must be HTTPS
- if (!this.checkHttps(url)) {
+ if (!this.checkHttps(url, allowHttp)) {
throw new IdentifiableError('0bedd29b-e3bf-4604-af51-d3352e2518af', `invalid url ${url}: unsupported protocol ${url.protocol}`);
}
@@ -255,12 +255,12 @@ export class UtilityService {
* Based on check-https.ts.
*/
@bindThis
- public checkHttps(url: string | URL): boolean {
+ public checkHttps(url: string | URL, allowHttp = false): boolean {
const isNonProd = this.envService.env.NODE_ENV !== 'production';
try {
const proto = new URL(url).protocol;
- return proto === 'https:' || (proto === 'http:' && isNonProd);
+ return proto === 'https:' || (proto === 'http:' && (isNonProd || allowHttp));
} catch {
// Invalid URLs don't "count" as HTTPS
return false;