mirror of
https://git.boykissers.com/pawkey/pawkey-sk.git
synced 2025-12-20 04:04:16 +00:00
161 lines
5.1 KiB
TypeScript
161 lines
5.1 KiB
TypeScript
/*
|
|
* SPDX-FileCopyrightText: hazelnoot and other Sharkey contributors
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
|
|
import { describe, jest } from '@jest/globals';
|
|
import type { Mock } from 'jest-mock';
|
|
import type { PrivateNetwork } from '@/config.js';
|
|
import type { Socket } from 'net';
|
|
import { HttpRequestService, isAllowedPrivateIp, isPrivateUrl, resolveIp, validateSocketConnect } from '@/core/HttpRequestService.js';
|
|
import { parsePrivateNetworks } from '@/config.js';
|
|
|
|
describe(HttpRequestService, () => {
|
|
let allowedPrivateNetworks: PrivateNetwork[] | undefined;
|
|
|
|
beforeEach(() => {
|
|
allowedPrivateNetworks = parsePrivateNetworks([
|
|
'10.0.0.1/32',
|
|
{ network: '127.0.0.1/32', ports: [1] },
|
|
{ network: '127.0.0.1/32', ports: [3, 4, 5] },
|
|
]);
|
|
});
|
|
|
|
describe(isAllowedPrivateIp, () => {
|
|
it('should return false when ip public', () => {
|
|
const result = isAllowedPrivateIp(allowedPrivateNetworks, '74.125.127.100', 80);
|
|
expect(result).toBeFalsy();
|
|
});
|
|
|
|
it('should return false when ip private and port matches', () => {
|
|
const result = isAllowedPrivateIp(allowedPrivateNetworks, '127.0.0.1', 1);
|
|
expect(result).toBeFalsy();
|
|
});
|
|
|
|
it('should return false when ip private and all ports undefined', () => {
|
|
const result = isAllowedPrivateIp(allowedPrivateNetworks, '10.0.0.1', undefined);
|
|
expect(result).toBeFalsy();
|
|
});
|
|
|
|
it('should return true when ip private and no ports specified', () => {
|
|
const result = isAllowedPrivateIp(allowedPrivateNetworks, '10.0.0.2', 80);
|
|
expect(result).toBeTruthy();
|
|
});
|
|
|
|
it('should return true when ip private and port does not match', () => {
|
|
const result = isAllowedPrivateIp(allowedPrivateNetworks, '127.0.0.1', 80);
|
|
expect(result).toBeTruthy();
|
|
});
|
|
|
|
it('should return true when ip private and port is null but ports are specified', () => {
|
|
const result = isAllowedPrivateIp(allowedPrivateNetworks, '127.0.0.1', undefined);
|
|
expect(result).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
const fakeLookup = (host: string, _: unknown, callback: (err: Error | null, ip: string) => void) => {
|
|
if (host === 'localhost') {
|
|
callback(null, '127.0.0.1');
|
|
} else {
|
|
callback(null, '23.192.228.80');
|
|
}
|
|
};
|
|
|
|
describe(resolveIp, () => {
|
|
it('should parse inline IPs', async () => {
|
|
const result = await resolveIp(new URL('https://10.0.0.1'), fakeLookup);
|
|
expect(result.toString()).toEqual('10.0.0.1');
|
|
});
|
|
|
|
it('should resolve domain names', async () => {
|
|
const result = await resolveIp(new URL('https://localhost'), fakeLookup);
|
|
expect(result.toString()).toEqual('127.0.0.1');
|
|
});
|
|
});
|
|
|
|
describe(isPrivateUrl, () => {
|
|
it('should return false when URL is public host', async () => {
|
|
const result = await isPrivateUrl(new URL('https://example.com'), fakeLookup);
|
|
expect(result).toBe(false);
|
|
});
|
|
|
|
it('should return true when URL is private host', async () => {
|
|
const result = await isPrivateUrl(new URL('https://localhost'), fakeLookup);
|
|
expect(result).toBe(true);
|
|
});
|
|
|
|
it('should return false when IP is public', async () => {
|
|
const result = await isPrivateUrl(new URL('https://23.192.228.80'), fakeLookup);
|
|
expect(result).toBe(false);
|
|
});
|
|
|
|
it('should return true when IP is private', async () => {
|
|
const result = await isPrivateUrl(new URL('https://127.0.0.1'), fakeLookup);
|
|
expect(result).toBe(true);
|
|
});
|
|
|
|
it('should return true when IP is private with port and path', async () => {
|
|
const result = await isPrivateUrl(new URL('https://127.0.0.1:443/some/path'), fakeLookup);
|
|
expect(result).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('validateSocketConnect', () => {
|
|
let fakeSocket: Socket;
|
|
let fakeSocketMutable: {
|
|
remoteAddress: string | undefined;
|
|
remotePort: number | undefined;
|
|
destroy: Mock<(error?: Error) => void>;
|
|
};
|
|
|
|
beforeEach(() => {
|
|
fakeSocketMutable = {
|
|
remoteAddress: '74.125.127.100',
|
|
remotePort: 80,
|
|
destroy: jest.fn<(error?: Error) => void>(),
|
|
};
|
|
fakeSocket = fakeSocketMutable as unknown as Socket;
|
|
});
|
|
|
|
it('should accept when IP is empty', () => {
|
|
fakeSocketMutable.remoteAddress = undefined;
|
|
|
|
validateSocketConnect(allowedPrivateNetworks, fakeSocket);
|
|
|
|
expect(fakeSocket.destroy).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should accept when IP is invalid', () => {
|
|
fakeSocketMutable.remoteAddress = 'AB939ajd9jdajsdja8jj';
|
|
|
|
validateSocketConnect(allowedPrivateNetworks, fakeSocket);
|
|
|
|
expect(fakeSocket.destroy).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should accept when IP is valid', () => {
|
|
validateSocketConnect(allowedPrivateNetworks, fakeSocket);
|
|
|
|
expect(fakeSocket.destroy).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should accept when IP is private and port match', () => {
|
|
fakeSocketMutable.remoteAddress = '127.0.0.1';
|
|
fakeSocketMutable.remotePort = 1;
|
|
|
|
validateSocketConnect(allowedPrivateNetworks, fakeSocket);
|
|
|
|
expect(fakeSocket.destroy).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should reject when IP is private and port no match', () => {
|
|
fakeSocketMutable.remoteAddress = '127.0.0.1';
|
|
fakeSocketMutable.remotePort = 2;
|
|
|
|
validateSocketConnect(allowedPrivateNetworks, fakeSocket);
|
|
|
|
expect(fakeSocket.destroy).toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|