Permalink
Show file tree
Hide file tree
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Cleanup request handler
Squashed commit of the following:
commit 90368698c81ed731c6d9be8b02bfa8ad1b5edd3f
Author: rijkvanzanten <rijkvanzanten@me.com>
Date: Mon Feb 13 15:49:12 2023 -0500
Cleanup
commit 61514f4
Author: rijkvanzanten <rijkvanzanten@me.com>
Date: Mon Feb 13 15:44:15 2023 -0500
Rename to index
commit 38fe6b8
Author: rijkvanzanten <rijkvanzanten@me.com>
Date: Mon Feb 13 15:41:23 2023 -0500
Test coverage 100%
commit f2e36db
Author: rijkvanzanten <rijkvanzanten@me.com>
Date: Mon Feb 13 15:22:45 2023 -0500
Split up handler from validator
commit 800ac19
Author: Pascal Jufer <pascal-jufer@bluewin.ch>
Date: Mon Feb 13 20:44:48 2023 +0100
Use shared axios instance with URL check for outgoing requests- Loading branch information
1 parent
ea91c40
commit ff53d3e
Showing
11 changed files
with
223 additions
and
73 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| import { test, vi, afterEach, beforeEach, expect } from 'vitest'; | ||
| import { getAxios, _cache } from './index'; | ||
| import axios from 'axios'; | ||
| import type { AxiosInstance } from 'axios'; | ||
|
|
||
| vi.mock('axios'); | ||
|
|
||
| let mockAxiosInstance: AxiosInstance; | ||
|
|
||
| beforeEach(() => { | ||
| mockAxiosInstance = { | ||
| interceptors: { | ||
| response: { | ||
| use: vi.fn(), | ||
| }, | ||
| }, | ||
| } as unknown as AxiosInstance; | ||
|
|
||
| vi.mocked(axios.create).mockReturnValue(mockAxiosInstance); | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| vi.resetAllMocks(); | ||
| _cache.axiosInstance = null; | ||
| }); | ||
|
|
||
| test('Creates and returns new axios instance if cache is empty', async () => { | ||
| const instance = await getAxios(); | ||
| expect(axios.create).toHaveBeenCalled(); | ||
| expect(instance).toBe(mockAxiosInstance); | ||
| }); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import type { AxiosInstance } from 'axios'; | ||
| import { responseInterceptor } from './response-interceptor'; | ||
|
|
||
| export const _cache: { axiosInstance: AxiosInstance | null } = { | ||
| axiosInstance: null, | ||
| }; | ||
|
|
||
| export async function getAxios() { | ||
| if (!_cache.axiosInstance) { | ||
| const axios = (await import('axios')).default; | ||
| _cache.axiosInstance = axios.create(); | ||
| _cache.axiosInstance.interceptors.response.use(responseInterceptor); | ||
| } | ||
|
|
||
| return _cache.axiosInstance; | ||
| } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| import { randIp, randUrl } from '@ngneat/falso'; | ||
| import type { AxiosResponse } from 'axios'; | ||
| import { afterEach, beforeEach, expect, test, vi } from 'vitest'; | ||
| import { responseInterceptor } from './response-interceptor'; | ||
| import { validateIP } from './validate-ip'; | ||
|
|
||
| vi.mock('./validate-ip'); | ||
|
|
||
| let sample: { | ||
| remoteAddress: string; | ||
| url: string; | ||
| }; | ||
|
|
||
| let sampleResponseConfig: AxiosResponse<any, any>; | ||
|
|
||
| beforeEach(() => { | ||
| sample = { | ||
| remoteAddress: randIp(), | ||
| url: randUrl(), | ||
| }; | ||
|
|
||
| sampleResponseConfig = { | ||
| request: { | ||
| socket: { | ||
| remoteAddress: sample.remoteAddress, | ||
| }, | ||
| url: sample.url, | ||
| }, | ||
| } as AxiosResponse<any, any>; | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| vi.resetAllMocks(); | ||
| }); | ||
|
|
||
| test(`Calls validateIP with IP/url from axios request config`, async () => { | ||
| await responseInterceptor(sampleResponseConfig); | ||
| expect(validateIP).toHaveBeenCalledWith(sample.remoteAddress, sample.url); | ||
| }); | ||
|
|
||
| test(`Returns passed in config as-is`, async () => { | ||
| const config = await responseInterceptor(sampleResponseConfig); | ||
| expect(config).toBe(sampleResponseConfig); | ||
| }); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| import type { AxiosResponse } from 'axios'; | ||
| import { validateIP } from './validate-ip'; | ||
|
|
||
| export const responseInterceptor = async (config: AxiosResponse<any, any>) => { | ||
| await validateIP(config.request.socket.remoteAddress, config.request.url); | ||
| return config; | ||
| }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| import { randIp, randUrl } from '@ngneat/falso'; | ||
| import os from 'node:os'; | ||
| import { afterEach, beforeEach, expect, test, vi } from 'vitest'; | ||
| import { getEnv } from '../env'; | ||
| import { validateIP } from './validate-ip'; | ||
|
|
||
| vi.mock('../env'); | ||
| vi.mock('node:os'); | ||
|
|
||
| let sample: { | ||
| ip: string; | ||
| url: string; | ||
| }; | ||
|
|
||
| beforeEach(() => { | ||
| sample = { | ||
| ip: randIp(), | ||
| url: randUrl(), | ||
| }; | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| vi.resetAllMocks(); | ||
| }); | ||
|
|
||
| test(`Does nothing if IP is valid`, async () => { | ||
| vi.mocked(getEnv).mockReturnValue({ IMPORT_IP_DENY_LIST: [] }); | ||
| await validateIP(sample.ip, sample.url); | ||
| }); | ||
|
|
||
| test(`Throws error if passed IP is denylisted`, async () => { | ||
| vi.mocked(getEnv).mockReturnValue({ IMPORT_IP_DENY_LIST: [sample.ip] }); | ||
|
|
||
| try { | ||
| await validateIP(sample.ip, sample.url); | ||
| } catch (err: any) { | ||
| expect(err).toBeInstanceOf(Error); | ||
| expect(err.message).toBe(`Requested URL "${sample.url}" resolves to a denied IP address`); | ||
| } | ||
| }); | ||
|
|
||
| test(`Checks against IPs of local networkInterfaces if IP deny list contains 0.0.0.0`, async () => { | ||
| vi.mocked(getEnv).mockReturnValue({ IMPORT_IP_DENY_LIST: ['0.0.0.0'] }); | ||
| vi.mocked(os.networkInterfaces).mockReturnValue({}); | ||
| await validateIP(sample.ip, sample.url); | ||
| expect(os.networkInterfaces).toHaveBeenCalledOnce(); | ||
| }); | ||
|
|
||
| test(`Throws error if IP address matches resolved localhost IP`, async () => { | ||
| vi.mocked(getEnv).mockReturnValue({ IMPORT_IP_DENY_LIST: ['0.0.0.0'] }); | ||
| vi.mocked(os.networkInterfaces).mockReturnValue({ | ||
| fa0: undefined, | ||
| lo0: [ | ||
| { | ||
| address: '127.0.0.1', | ||
| netmask: '255.0.0.0', | ||
| family: 'IPv4', | ||
| mac: '00:00:00:00:00:00', | ||
| internal: true, | ||
| cidr: '127.0.0.1/8', | ||
| }, | ||
| ], | ||
| en0: [ | ||
| { | ||
| address: sample.ip, | ||
| netmask: '255.0.0.0', | ||
| family: 'IPv4', | ||
| mac: '00:00:00:00:00:00', | ||
| internal: true, | ||
| cidr: '127.0.0.1/8', | ||
| }, | ||
| ], | ||
| }); | ||
|
|
||
| try { | ||
| await validateIP(sample.ip, sample.url); | ||
| } catch (err: any) { | ||
| expect(err).toBeInstanceOf(Error); | ||
| expect(err.message).toBe(`Requested URL "${sample.url}" resolves to a denied IP address`); | ||
| } | ||
| }); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import os from 'node:os'; | ||
| import { getEnv } from '../env'; | ||
|
|
||
| export const validateIP = async (ip: string, url: string) => { | ||
| const env = getEnv(); | ||
|
|
||
| if (env.IMPORT_IP_DENY_LIST.includes(ip)) { | ||
| throw new Error(`Requested URL "${url}" resolves to a denied IP address`); | ||
| } | ||
|
|
||
| if (env.IMPORT_IP_DENY_LIST.includes('0.0.0.0')) { | ||
| const networkInterfaces = os.networkInterfaces(); | ||
|
|
||
| for (const networkInfo of Object.values(networkInterfaces)) { | ||
| if (!networkInfo) continue; | ||
|
|
||
| for (const info of networkInfo) { | ||
| if (info.address === ip) { | ||
| throw new Error(`Requested URL "${url}" resolves to a denied IP address`); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters