| @@ -8,7 +8,8 @@ import sanitize from 'sanitize-filename'; | |||
| 8 | import fetch from 'node-fetch'; | 8 | import fetch from 'node-fetch'; |
| 9 | 9 | ||
| 10 | import { UNSAFE_EXTENSIONS } from '../constants.js'; | 10 | import { UNSAFE_EXTENSIONS } from '../constants.js'; |
| 11 | import { clientRelativePath } from '../util.js'; | 11 | import { clientRelativePath, isValidUrl } from '../util.js'; |
| 12 | import { getHostFromUrl, isHostWhitelisted } from './content-manager.js'; | ||
| 12 | 13 | ||
| 13 | const VALID_CATEGORIES = ['bgm', 'ambient', 'blip', 'live2d', 'vrm', 'character', 'temp']; | 14 | const VALID_CATEGORIES = ['bgm', 'ambient', 'blip', 'live2d', 'vrm', 'character', 'temp']; |
| 14 | 15 | ||
| @@ -189,9 +190,21 @@ router.post('/get', async (request, response) => { | |||
| 189 | * @returns {void} | 190 | * @returns {void} |
| 190 | */ | 191 | */ |
| 191 | router.post('/download', async (request, response) => { | 192 | router.post('/download', async (request, response) => { |
| 192 | const url = request.body.url; | 193 | try { |
| 194 | if (!isValidUrl(request.body.url)) { | ||
| 195 | console.warn('Asset download failed: Must be a valid URL'); | ||
| 196 | return response.sendStatus(400); | ||
| 197 | } | ||
| 198 | |||
| 199 | const url = String(request.body.url); | ||
| 193 | const inputCategory = request.body.category; | 200 | const inputCategory = request.body.category; |
| 194 | 201 | ||
| 202 | const host = getHostFromUrl(url); | ||
| 203 | if (!isHostWhitelisted(host)) { | ||
| 204 | console.error(`Received an import for "${host}", but site is not whitelisted. This domain must be added to the config key "whitelistImportDomains" to allow import from this source.`); | ||
| 205 | return response.sendStatus(404); | ||
| 206 | } | ||
| 207 | |||
| 195 | // Check category | 208 | // Check category |
| 196 | let category = null; | 209 | let category = null; |
| 197 | for (let i of VALID_CATEGORIES) | 210 | for (let i of VALID_CATEGORIES) |
| @@ -213,7 +226,6 @@ router.post('/download', async (request, response) => { | |||
| 213 | const file_path = path.join(request.user.directories.assets, category, request.body.filename); | 226 | const file_path = path.join(request.user.directories.assets, category, request.body.filename); |
| 214 | console.info('Request received to download', url, 'to', file_path); | 227 | console.info('Request received to download', url, 'to', file_path); |
| 215 | 228 | ||
| 216 | try { | ||
| 217 | // Download to temp | 229 | // Download to temp |
| 218 | const res = await fetch(url); | 230 | const res = await fetch(url); |
| 219 | if (!res.ok || res.body === null) { | 231 | if (!res.ok || res.body === null) { |
| @@ -872,7 +872,7 @@ function getUuidFromUrl(url) { | |||
| 872 | * @param {String} url URL to strip | 872 | * @param {String} url URL to strip |
| 873 | * @returns {String} Domain name | 873 | * @returns {String} Domain name |
| 874 | */ | 874 | */ |
| 875 | function getHostFromUrl(url) { | 875 | export function getHostFromUrl(url) { |
| 876 | try { | 876 | try { |
| 877 | const urlObj = new URL(url); | 877 | const urlObj = new URL(url); |
| 878 | return urlObj.hostname; | 878 | return urlObj.hostname; |
| @@ -886,7 +886,7 @@ function getHostFromUrl(url) { | |||
| 886 | * @param {String} host Host to check | 886 | * @param {String} host Host to check |
| 887 | * @returns {boolean} If the host is on the whitelist. | 887 | * @returns {boolean} If the host is on the whitelist. |
| 888 | */ | 888 | */ |
| 889 | function isHostWhitelisted(host) { | 889 | export function isHostWhitelisted(host) { |
| 890 | return WHITELIST_GENERIC_URL_DOWNLOAD_SOURCES.includes(host); | 890 | return WHITELIST_GENERIC_URL_DOWNLOAD_SOURCES.includes(host); |
| 891 | } | 891 | } |
| 892 | 892 | ||