diff options
-rw-r--r-- | client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts | 2 | ||||
-rw-r--r-- | server/helpers/dns.ts | 29 | ||||
-rw-r--r-- | server/middlewares/validators/videos/video-imports.ts | 18 | ||||
-rw-r--r-- | server/tests/api/check-params/video-imports.ts | 3 | ||||
-rw-r--r-- | server/tests/helpers/dns.ts | 17 | ||||
-rw-r--r-- | server/tests/helpers/index.ts | 1 |
6 files changed, 57 insertions, 13 deletions
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts index 4e77cac47..fb6f2601b 100644 --- a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts +++ b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts | |||
@@ -9,7 +9,7 @@ import { genericUploadErrorHandler, scrollToTop } from '@app/helpers' | |||
9 | import { FormValidatorService } from '@app/shared/shared-forms' | 9 | import { FormValidatorService } from '@app/shared/shared-forms' |
10 | import { BytesPipe, Video, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main' | 10 | import { BytesPipe, Video, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main' |
11 | import { LoadingBarService } from '@ngx-loading-bar/core' | 11 | import { LoadingBarService } from '@ngx-loading-bar/core' |
12 | import { HttpStatusCode, VideoCreateResult, VideoPrivacy } from '@shared/models' | 12 | import { HttpStatusCode, VideoCreateResult } from '@shared/models' |
13 | import { UploaderXFormData } from './uploaderx-form-data' | 13 | import { UploaderXFormData } from './uploaderx-form-data' |
14 | import { VideoSend } from './video-send' | 14 | import { VideoSend } from './video-send' |
15 | 15 | ||
diff --git a/server/helpers/dns.ts b/server/helpers/dns.ts new file mode 100644 index 000000000..da8b666c2 --- /dev/null +++ b/server/helpers/dns.ts | |||
@@ -0,0 +1,29 @@ | |||
1 | import { lookup } from 'dns' | ||
2 | import { parse as parseIP } from 'ipaddr.js' | ||
3 | |||
4 | function dnsLookupAll (hostname: string) { | ||
5 | return new Promise<string[]>((res, rej) => { | ||
6 | lookup(hostname, { family: 0, all: true }, (err, adresses) => { | ||
7 | if (err) return rej(err) | ||
8 | |||
9 | return res(adresses.map(a => a.address)) | ||
10 | }) | ||
11 | }) | ||
12 | } | ||
13 | |||
14 | async function isResolvingToUnicastOnly (hostname: string) { | ||
15 | const addresses = await dnsLookupAll(hostname) | ||
16 | |||
17 | for (const address of addresses) { | ||
18 | const parsed = parseIP(address) | ||
19 | |||
20 | if (parsed.range() !== 'unicast') return false | ||
21 | } | ||
22 | |||
23 | return true | ||
24 | } | ||
25 | |||
26 | export { | ||
27 | dnsLookupAll, | ||
28 | isResolvingToUnicastOnly | ||
29 | } | ||
diff --git a/server/middlewares/validators/videos/video-imports.ts b/server/middlewares/validators/videos/video-imports.ts index a3a5cc531..9c6d213c4 100644 --- a/server/middlewares/validators/videos/video-imports.ts +++ b/server/middlewares/validators/videos/video-imports.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import express from 'express' | 1 | import express from 'express' |
2 | import { body, param } from 'express-validator' | 2 | import { body, param } from 'express-validator' |
3 | import { isValid as isIPValid, parse as parseIP } from 'ipaddr.js' | 3 | import { isResolvingToUnicastOnly } from '@server/helpers/dns' |
4 | import { isPreImportVideoAccepted } from '@server/lib/moderation' | 4 | import { isPreImportVideoAccepted } from '@server/lib/moderation' |
5 | import { Hooks } from '@server/lib/plugins/hooks' | 5 | import { Hooks } from '@server/lib/plugins/hooks' |
6 | import { MUserAccountId, MVideoImport } from '@server/types/models' | 6 | import { MUserAccountId, MVideoImport } from '@server/types/models' |
@@ -76,17 +76,13 @@ const videoImportAddValidator = getCommonVideoEditAttributes().concat([ | |||
76 | if (req.body.targetUrl) { | 76 | if (req.body.targetUrl) { |
77 | const hostname = new URL(req.body.targetUrl).hostname | 77 | const hostname = new URL(req.body.targetUrl).hostname |
78 | 78 | ||
79 | if (isIPValid(hostname)) { | 79 | if (await isResolvingToUnicastOnly(hostname) !== true) { |
80 | const parsed = parseIP(hostname) | 80 | cleanUpReqFiles(req) |
81 | 81 | ||
82 | if (parsed.range() !== 'unicast') { | 82 | return res.fail({ |
83 | cleanUpReqFiles(req) | 83 | status: HttpStatusCode.FORBIDDEN_403, |
84 | 84 | message: 'Cannot use non unicast IP as targetUrl.' | |
85 | return res.fail({ | 85 | }) |
86 | status: HttpStatusCode.FORBIDDEN_403, | ||
87 | message: 'Cannot use non unicast IP as targetUrl.' | ||
88 | }) | ||
89 | } | ||
90 | } | 86 | } |
91 | } | 87 | } |
92 | 88 | ||
diff --git a/server/tests/api/check-params/video-imports.ts b/server/tests/api/check-params/video-imports.ts index 156a612ee..7893f5cc5 100644 --- a/server/tests/api/check-params/video-imports.ts +++ b/server/tests/api/check-params/video-imports.ts | |||
@@ -120,7 +120,8 @@ describe('Test video imports API validator', function () { | |||
120 | 'http://127.0.0.1', | 120 | 'http://127.0.0.1', |
121 | 'http://127.0.0.1/hello', | 121 | 'http://127.0.0.1/hello', |
122 | 'https://192.168.1.42', | 122 | 'https://192.168.1.42', |
123 | 'http://192.168.1.42' | 123 | 'http://192.168.1.42', |
124 | 'http://127.0.0.1.cpy.re' | ||
124 | ] | 125 | ] |
125 | 126 | ||
126 | for (const targetUrl of targetUrls) { | 127 | for (const targetUrl of targetUrls) { |
diff --git a/server/tests/helpers/dns.ts b/server/tests/helpers/dns.ts new file mode 100644 index 000000000..309de5426 --- /dev/null +++ b/server/tests/helpers/dns.ts | |||
@@ -0,0 +1,17 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | ||
2 | |||
3 | import 'mocha' | ||
4 | import { expect } from 'chai' | ||
5 | import { isResolvingToUnicastOnly } from '@server/helpers/dns' | ||
6 | |||
7 | describe('DNS helpers', function () { | ||
8 | |||
9 | it('Should correctly check unicast IPs', async function () { | ||
10 | expect(await isResolvingToUnicastOnly('cpy.re')).to.be.true | ||
11 | expect(await isResolvingToUnicastOnly('framasoft.org')).to.be.true | ||
12 | expect(await isResolvingToUnicastOnly('8.8.8.8')).to.be.true | ||
13 | |||
14 | expect(await isResolvingToUnicastOnly('127.0.0.1')).to.be.false | ||
15 | expect(await isResolvingToUnicastOnly('127.0.0.1.cpy.re')).to.be.false | ||
16 | }) | ||
17 | }) | ||
diff --git a/server/tests/helpers/index.ts b/server/tests/helpers/index.ts index 91d11e25d..951208842 100644 --- a/server/tests/helpers/index.ts +++ b/server/tests/helpers/index.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | import './image' | 1 | import './image' |
2 | import './core-utils' | 2 | import './core-utils' |
3 | import './dns' | ||
3 | import './comment-model' | 4 | import './comment-model' |
4 | import './markdown' | 5 | import './markdown' |
5 | import './request' | 6 | import './request' |