From 56c70d4b0e5a6e72a78bac7e471fa240d49c07c8 Mon Sep 17 00:00:00 2001 From: Liming Jin Date: Fri, 19 Apr 2019 21:52:28 +0800 Subject: [PATCH 1/2] fix: logo field in configuration cannot use the local file --- src/api/web/index.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/api/web/index.js b/src/api/web/index.js index 2a139df7d..19478ad6d 100644 --- a/src/api/web/index.js +++ b/src/api/web/index.js @@ -43,6 +43,25 @@ module.exports = function(config, auth, storage) { const indexTemplate = path.join(themePath, 'index.html'); const template = fs.readFileSync(indexTemplate).toString(); + // Logo + let logoURI = _.get(config, 'web.logo') ? config.web.logo : ''; + if (logoURI && !/^(https?:)?\/\//.test(logoURI)) { + // URI that not starts with "http://", "https://" or "//" + logoURI = path.join('/-/static/', path.basename(logoURI)); + router.get(logoURI, function(req, res, next) { + res.sendFile(path.resolve(config.web.logo), function(err) { + if (!err) { + return; + } + if (err.status === HTTP_STATUS.NOT_FOUND) { + next(); + } else { + next(err); + } + }); + }); + } + // Static router.get('/-/static/*', function(req, res, next) { const filename = req.params[0]; @@ -66,7 +85,7 @@ module.exports = function(config, auth, storage) { .replace(/ToReplaceByVerdaccio/g, base) .replace(/ToReplaceByVersion/g, pkgJSON.version) .replace(/ToReplaceByTitle/g, _.get(config, 'web.title') ? config.web.title : WEB_TITLE) - .replace(/ToReplaceByLogo/g, _.get(config, 'web.logo') ? config.web.logo : '') + .replace(/ToReplaceByLogo/g, logoURI) .replace(/ToReplaceByScope/g, _.get(config, 'web.scope') ? config.web.scope : ''); res.setHeader('Content-Type', HEADERS.TEXT_HTML); From 8e5203be9e326c312cc368883cade2918a7bc9e5 Mon Sep 17 00:00:00 2001 From: Liming Jin Date: Sat, 27 Apr 2019 00:35:39 +0800 Subject: [PATCH 2/2] test: add unit test for the HTTP protocol check --- src/api/web/index.js | 45 +++++++++++++++++-------------------- src/lib/utils.js | 8 +++++++ test/unit/api/utils.spec.js | 23 ++++++++++++++++++- 3 files changed, 50 insertions(+), 26 deletions(-) diff --git a/src/api/web/index.js b/src/api/web/index.js index d4c3e6256..5dda2d63a 100644 --- a/src/api/web/index.js +++ b/src/api/web/index.js @@ -8,7 +8,7 @@ import fs from 'fs'; import path from 'path'; import express from 'express'; -import { combineBaseUrl, getWebProtocol } from '../../lib/utils'; +import { combineBaseUrl, getWebProtocol, isHTTPProtocol } from '../../lib/utils'; import Search from '../../lib/search'; import { HEADERS, HTTP_STATUS, WEB_TITLE } from '../../lib/constants'; import loadPlugin from '../../lib/plugin-loader'; @@ -32,6 +32,17 @@ export function loadTheme(config) { } } +const sendFileCallback = next => err => { + if (!err) { + return; + } + if (err.status === HTTP_STATUS.NOT_FOUND) { + next(); + } else { + next(err); + } +}; + module.exports = function(config, auth, storage) { Search.configureStorage(storage); /* eslint new-cap:off */ @@ -45,38 +56,22 @@ module.exports = function(config, auth, storage) { // Logo let logoURI = _.get(config, 'web.logo') ? config.web.logo : ''; - if (logoURI && !/^(https?:)?\/\//.test(logoURI)) { - // URI that not starts with "http://", "https://" or "//" - logoURI = path.join('/-/static/', path.basename(logoURI)); + if (logoURI && !isHTTPProtocol(logoURI)) { + // URI related to a local file + + // Note: `path.join` will break on Windows, because it transforms `/` to `\` + // Use POSIX version `path.posix.join` instead. + logoURI = path.posix.join('/-/static/', path.basename(logoURI)); router.get(logoURI, function(req, res, next) { - res.sendFile(path.resolve(config.web.logo), function(err) { - if (!err) { - return; - } - if (err.status === HTTP_STATUS.NOT_FOUND) { - next(); - } else { - next(err); - } - }); + res.sendFile(path.resolve(config.web.logo), sendFileCallback(next)); }); } // Static router.get('/-/static/*', function(req, res, next) { const filename = req.params[0]; - const file = `${themePath}/${filename}`; - res.sendFile(file, function(err) { - if (!err) { - return; - } - if (err.status === HTTP_STATUS.NOT_FOUND) { - next(); - } else { - next(err); - } - }); + res.sendFile(file, sendFileCallback(next)); }); function renderHTML(req, res) { diff --git a/src/lib/utils.js b/src/lib/utils.js index 202007aa8..ab4614d08 100644 --- a/src/lib/utils.js +++ b/src/lib/utils.js @@ -547,3 +547,11 @@ export function formatAuthor(author: any) { return authorDetails; } + +/** + * Check if URI is starting with "http://", "https://" or "//" + * @param {string} uri + */ +export function isHTTPProtocol(uri: string): boolean { + return /^(https?:)?\/\//.test(uri); +} diff --git a/test/unit/api/utils.spec.js b/test/unit/api/utils.spec.js index a4b3737cd..f8810ba2a 100644 --- a/test/unit/api/utils.spec.js +++ b/test/unit/api/utils.spec.js @@ -14,7 +14,8 @@ import { getWebProtocol, getVersionFromTarball, sortByName, - formatAuthor + formatAuthor, + isHTTPProtocol, } from '../../../src/lib/utils'; import { DIST_TAGS, DEFAULT_USER } from '../../../src/lib/constants'; import Logger, { setup } from '../../../src/lib/logger'; @@ -332,6 +333,26 @@ describe('Utilities', () => { expect(url).toMatch('/-/static/logo.png'); }); + + test('should check HTTP protocol correctly', () => { + expect(isHTTPProtocol('http://domain.com/-/static/logo.png')).toBeTruthy(); + expect(isHTTPProtocol('https://www.domain.com/-/static/logo.png')).toBeTruthy(); + expect(isHTTPProtocol('//domain.com/-/static/logo.png')).toBeTruthy(); + expect(isHTTPProtocol('file:///home/user/logo.png')).toBeFalsy(); + expect(isHTTPProtocol('file:///F:/home/user/logo.png')).toBeFalsy(); + // Note that uses ftp protocol in src was deprecated in modern browsers + expect(isHTTPProtocol('ftp://1.2.3.4/home/user/logo.png')).toBeFalsy(); + expect(isHTTPProtocol('./logo.png')).toBeFalsy(); + expect(isHTTPProtocol('.\\logo.png')).toBeFalsy(); + expect(isHTTPProtocol('../logo.png')).toBeFalsy(); + expect(isHTTPProtocol('..\\logo.png')).toBeFalsy(); + expect(isHTTPProtocol('../../static/logo.png')).toBeFalsy(); + expect(isHTTPProtocol('..\\..\\static\\logo.png')).toBeFalsy(); + expect(isHTTPProtocol('logo.png')).toBeFalsy(); + expect(isHTTPProtocol('.logo.png')).toBeFalsy(); + expect(isHTTPProtocol('/static/logo.png')).toBeFalsy(); + expect(isHTTPProtocol('F:\\static\\logo.png')).toBeFalsy(); + }); }); describe('User utilities', () => {