From a1883a5480f0d40ce13b282467cb5f0f75ee9891 Mon Sep 17 00:00:00 2001 From: s0ph1e Date: Wed, 23 Sep 2020 21:32:45 +0300 Subject: [PATCH 1/2] Use sanitizeFilaname in filename generators --- lib/filename-generator/by-site-structure.js | 7 +- lib/filename-generator/by-type.js | 3 +- package-lock.json | 21 +++ package.json | 1 + .../by-site-structure-test.js | 95 +++++------ test/unit/filename-generator/by-type-test.js | 149 +++++++++--------- 6 files changed, 157 insertions(+), 119 deletions(-) diff --git a/lib/filename-generator/by-site-structure.js b/lib/filename-generator/by-site-structure.js index 93eda3b6..a504c288 100644 --- a/lib/filename-generator/by-site-structure.js +++ b/lib/filename-generator/by-site-structure.js @@ -1,6 +1,7 @@ const _ = require('lodash'); const path = require('path'); const url = require('url'); +const sanitizeFilename = require('sanitize-filename'); const utils = require('../utils'); const resourceTypes = require('../config/resource-types'); const resourceTypeExtensions = require('../config/resource-ext-by-type'); @@ -51,6 +52,10 @@ function sanitizeFilepath (filePath) { filePath = path.normalize(filePath); let pathParts = filePath.split(path.sep); pathParts = _.pull(pathParts, '..'); - pathParts[pathParts.length - 1] = utils.shortenFilename(_.last(pathParts)); + pathParts[pathParts.length - 1] = getSanitizedFilename(_.last(pathParts)); return pathParts.join(path.sep); } + +function getSanitizedFilename (filename) { + return utils.shortenFilename(sanitizeFilename(filename, {replacement: '_'})); +} diff --git a/lib/filename-generator/by-type.js b/lib/filename-generator/by-type.js index 97c54f15..d2fcc476 100644 --- a/lib/filename-generator/by-type.js +++ b/lib/filename-generator/by-type.js @@ -1,5 +1,6 @@ const _ = require('lodash'); const path = require('path'); +const sanitizeFilename = require('sanitize-filename'); const utils = require('../utils'); const typeExtensions = require('../config/resource-ext-by-type'); @@ -7,7 +8,7 @@ module.exports = function generateFilename (resource, {subdirectories, defaultFi const occupiedNames = getSubDirectoryNames({subdirectories}).concat(occupiedFileNames); let filename = getFilenameForResource(resource, {subdirectories, defaultFilename}); - filename = utils.shortenFilename(filename); + filename = utils.shortenFilename(sanitizeFilename(filename, {replacement: '_'})); const extension = utils.getFilenameExtension(filename); const directory = getDirectoryByExtension(extension, {subdirectories, defaultFilename}); diff --git a/package-lock.json b/package-lock.json index 560c9e22..d278142b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3028,6 +3028,14 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "sanitize-filename": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", + "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "requires": { + "truncate-utf8-bytes": "^1.0.0" + } + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -3420,6 +3428,14 @@ "punycode": "^2.1.1" } }, + "truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha1-QFkjkJWS1W94pYGENLC3hInKXys=", + "requires": { + "utf8-byte-length": "^1.0.1" + } + }, "tslib": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", @@ -3482,6 +3498,11 @@ "punycode": "^2.1.0" } }, + "utf8-byte-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", + "integrity": "sha1-9F8VDExm7uloGGUFq5P8u4rWv2E=" + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index 84bd76e9..d66a85c6 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "normalize-url": "^4.0.0", "p-queue": "^6.6.1", "request": "^2.85.0", + "sanitize-filename": "^1.6.3", "srcset": "^2.0.0" }, "devDependencies": { diff --git a/test/unit/filename-generator/by-site-structure-test.js b/test/unit/filename-generator/by-site-structure-test.js index 81356bb4..29945b17 100644 --- a/test/unit/filename-generator/by-site-structure-test.js +++ b/test/unit/filename-generator/by-site-structure-test.js @@ -1,103 +1,108 @@ -var _ = require('lodash'); -var should = require('should'); +const _ = require('lodash'); +const should = require('should'); require('../../utils/assertions'); -var sinon = require('sinon'); -var Resource = require('../../../lib/resource'); -var bySiteStructureFilenameGenerator = require('../../../lib/filename-generator/by-site-structure'); +const sinon = require('sinon'); +const Resource = require('../../../lib/resource'); +const bySiteStructureFilenameGenerator = require('../../../lib/filename-generator/by-site-structure'); -var options = { defaultFilename: 'index.html' }; +const options = { defaultFilename: 'index.html' }; -describe('FilenameGenerator: bySiteStructure', function() { - it('should return the normalized relative path of the resource url', function(){ - var r1 = new Resource('http://example.com/some/path/a.png'); +describe('FilenameGenerator: bySiteStructure', () => { + it('should return the normalized relative path of the resource url', () =>{ + const r1 = new Resource('http://example.com/some/path/a.png'); bySiteStructureFilenameGenerator(r1, options).should.equalFileSystemPath('example.com/some/path/a.png'); - var r2 = new Resource('http://example.com/a.png'); + const r2 = new Resource('http://example.com/a.png'); bySiteStructureFilenameGenerator(r2, options).should.equalFileSystemPath('example.com/a.png'); - var r3 = new Resource('http://example.com/some/path/../images/a.png'); + const r3 = new Resource('http://example.com/some/path/../images/a.png'); bySiteStructureFilenameGenerator(r3, options).should.equalFileSystemPath('example.com/some/images/a.png'); }); - it('should replace the colon, for url with port number', function(){ - var r1 = new Resource('http://example.com:8080/some/path/a.png'); + it('should replace the colon, for url with port number', () =>{ + const r1 = new Resource('http://example.com:8080/some/path/a.png'); bySiteStructureFilenameGenerator(r1, options).should.equalFileSystemPath('example.com_8080/some/path/a.png'); }); - it('should add the defaultFilename to the path, for html resources without extension', function(){ - var isHtmlMock = sinon.stub().returns(true); + it('should remove not allowed characters from filename', () => { + const r1 = new Resource('http://example.com/some/path/<*a*>.png'); + bySiteStructureFilenameGenerator(r1, options).should.equalFileSystemPath('example.com/some/path/__a__.png'); + }); + + it('should add the defaultFilename to the path, for html resources without extension', () =>{ + const isHtmlMock = sinon.stub().returns(true); - var r1 = new Resource('http://example.com/some/path/'); + const r1 = new Resource('http://example.com/some/path/'); r1.isHtml = isHtmlMock; bySiteStructureFilenameGenerator(r1, options).should.equalFileSystemPath('example.com/some/path/index.html'); - var r2 = new Resource('http://example.com/some/path'); + const r2 = new Resource('http://example.com/some/path'); r2.isHtml = isHtmlMock; bySiteStructureFilenameGenerator(r2, options).should.equalFileSystemPath('example.com/some/path/index.html'); - var r3 = new Resource('http://example.com'); + const r3 = new Resource('http://example.com'); r3.isHtml = isHtmlMock; bySiteStructureFilenameGenerator(r3, options).should.equalFileSystemPath('example.com/index.html'); }); - it('should add the defaultFilename to the path, for html resources with wrong extension', function(){ - var isHtmlMock = sinon.stub().returns(true); + it('should add the defaultFilename to the path, for html resources with wrong extension', () =>{ + const isHtmlMock = sinon.stub().returns(true); - var r1 = new Resource('http://example.com/some/path/test.com'); + const r1 = new Resource('http://example.com/some/path/test.com'); r1.isHtml = isHtmlMock; bySiteStructureFilenameGenerator(r1, options).should.equalFileSystemPath('example.com/some/path/test.com/index.html'); }); - it('should normalize to safe relative paths, without ..', function(){ - var r = new Resource('http://example.com/some/path/../../../../images/a.png'); + it('should normalize to safe relative paths, without ..', () =>{ + const r = new Resource('http://example.com/some/path/../../../../images/a.png'); bySiteStructureFilenameGenerator(r, options).should.equalFileSystemPath('example.com/images/a.png'); }); - it('should not replace thrice dot in filenames', function() { + it('should not replace thrice dot in filenames', () => { // if it replaces them we receive 'some/path/../../../../etc/passwd' // path.resolve('some/path/../../../../etc/passwd'); = '/etc/passwd' => which is not safe - var r = new Resource('http://example.com/some/path/.../.../.../.../etc/passwd'); + const r = new Resource('http://example.com/some/path/.../.../.../.../etc/passwd'); bySiteStructureFilenameGenerator(r, options).should.equalFileSystemPath('example.com/some/path/.../.../.../.../etc/passwd'); }); - it('should shorten filename', function() { - var resourceFilename = _.repeat('1', 1000) + '.png'; - var r = new Resource('http://example.com/' + resourceFilename); - var filename = bySiteStructureFilenameGenerator(r, options); + it('should shorten filename', () => { + const resourceFilename = _.repeat('1', 1000) + '.png'; + const r = new Resource('http://example.com/' + resourceFilename); + const filename = bySiteStructureFilenameGenerator(r, options); should(filename.length).be.lessThan(255); }); - it('should shorten filename if resource is html without ext and default name is too long', function() { - var defaultFilename = _.repeat('1', 1000) + '.html'; - var r = new Resource('http://example.com/path'); + it('should shorten filename if resource is html without ext and default name is too long', () => { + const defaultFilename = _.repeat('1', 1000) + '.html'; + const r = new Resource('http://example.com/path'); r.isHtml = sinon.stub().returns(true); - var filepath = bySiteStructureFilenameGenerator(r, { defaultFilename: defaultFilename }); - var filename = _.last(filepath.split('/')); + const filepath = bySiteStructureFilenameGenerator(r, { defaultFilename: defaultFilename }); + const filename = _.last(filepath.split('/')); should(filename.length).be.lessThan(255); }); - it('should return decoded filepath', function() { - var r = new Resource('https://developer.mozilla.org/ru/docs/JavaScript_%D1%88%D0%B5%D0%BB%D0%BB%D1%8B'); - var filename = bySiteStructureFilenameGenerator(r, options); + it('should return decoded filepath', () => { + const r = new Resource('https://developer.mozilla.org/ru/docs/JavaScript_%D1%88%D0%B5%D0%BB%D0%BB%D1%8B'); + const filename = bySiteStructureFilenameGenerator(r, options); filename.should.equalFileSystemPath('developer.mozilla.org/ru/docs/JavaScript_шеллы'); - var r2 = new Resource('https://developer.mozilla.org/Hello%20G%C3%BCnter.png'); - var filename2 = bySiteStructureFilenameGenerator(r2, options); + const r2 = new Resource('https://developer.mozilla.org/Hello%20G%C3%BCnter.png'); + const filename2 = bySiteStructureFilenameGenerator(r2, options); filename2.should.equalFileSystemPath('developer.mozilla.org/Hello Günter.png'); }); - it('should keep query strings', function () { - var isHtmlMock = sinon.stub().returns(true); + it('should keep query strings', () => { + const isHtmlMock = sinon.stub().returns(true); - var r1 = new Resource('http://example.com/path?q=test'); + const r1 = new Resource('http://example.com/path?q=test'); r1.isHtml = isHtmlMock; bySiteStructureFilenameGenerator(r1, options).should.equalFileSystemPath('example.com/path/q=test.html'); - var r2 = new Resource('http://example.com/path?q1=test1&q2=test2'); + const r2 = new Resource('http://example.com/path?q1=test1&q2=test2'); r2.isHtml = isHtmlMock; bySiteStructureFilenameGenerator(r2, options).should.equalFileSystemPath('example.com/path/q1=test1&q2=test2.html'); - var r3 = new Resource('http://example.com/path/picture.png?q1=test1&q2=test2'); + const r3 = new Resource('http://example.com/path/picture.png?q1=test1&q2=test2'); bySiteStructureFilenameGenerator(r3, options).should.equalFileSystemPath('example.com/path/picture_q1=test1&q2=test2.png'); - }) + }); }); diff --git a/test/unit/filename-generator/by-type-test.js b/test/unit/filename-generator/by-type-test.js index 9672e753..d9081d42 100644 --- a/test/unit/filename-generator/by-type-test.js +++ b/test/unit/filename-generator/by-type-test.js @@ -1,143 +1,148 @@ -var _ = require('lodash'); -var should = require('should'); +const _ = require('lodash'); +const should = require('should'); require('../../utils/assertions'); -var sinon = require('sinon'); -var Resource = require('../../../lib/resource'); -var byTypeFilenameGenerator = require('../../../lib/filename-generator/by-type'); - -describe('FilenameGenerator: byType', function() { - it('should return resource filename', function() { - var r = new Resource('http://example.com/a.png', 'b.png'); - var filename = byTypeFilenameGenerator(r, {}, []); +const sinon = require('sinon'); +const Resource = require('../../../lib/resource'); +const byTypeFilenameGenerator = require('../../../lib/filename-generator/by-type'); + +describe('FilenameGenerator: byType', () => { + it('should return resource filename', () => { + const r = new Resource('http://example.com/a.png', 'b.png'); + const filename = byTypeFilenameGenerator(r, {}, []); filename.should.equalFileSystemPath('b.png'); }); - it('should return url-based filename if resource has no filename', function() { - var r = new Resource('http://example.com/a.png'); - var filename = byTypeFilenameGenerator(r, {}, []); + it('should return url-based filename if resource has no filename', () => { + const r = new Resource('http://example.com/a.png'); + const filename = byTypeFilenameGenerator(r, {}, []); filename.should.equalFileSystemPath('a.png'); }); - it('should return url-based filename if resource has empty filename', function() { - var r = new Resource('http://example.com/a.png', ''); - var filename = byTypeFilenameGenerator(r, {}, []); + it('should return url-based filename if resource has empty filename', () => { + const r = new Resource('http://example.com/a.png', ''); + const filename = byTypeFilenameGenerator(r, {}, []); filename.should.equalFileSystemPath('a.png'); }); - it('should add missed extensions for html resources', function () { - var r = new Resource('http://example.com/about', ''); + it('should add missed extensions for html resources', () => { + const r = new Resource('http://example.com/about', ''); r.getType = sinon.stub().returns('html'); - var filename = byTypeFilenameGenerator(r, {}, []); + const filename = byTypeFilenameGenerator(r, {}, []); filename.should.equalFileSystemPath('about.html'); }); - it('should add missed extensions for css resources', function () { - var r = new Resource('http://example.com/css', ''); + it('should add missed extensions for css resources', () => { + const r = new Resource('http://example.com/css', ''); r.getType = sinon.stub().returns('css'); - var filename = byTypeFilenameGenerator(r, {}, []); + const filename = byTypeFilenameGenerator(r, {}, []); filename.should.equalFileSystemPath('css.css'); }); - it('should add missed extensions for js resources', function () { - var r = new Resource('http://example.com/js', ''); + it('should add missed extensions for js resources', () => { + const r = new Resource('http://example.com/js', ''); r.getType = sinon.stub().returns('js'); - var filename = byTypeFilenameGenerator(r, {}, []); + const filename = byTypeFilenameGenerator(r, {}, []); filename.should.equalFileSystemPath('js.js'); }); - it('should not add missed extensions for other resources', function () { - var r = new Resource('http://1.gravatar.com/avatar/4d63e4a045c7ff22accc33dc08442f86?s=140&d=%2Fwp-content%2Fuploads%2F2015%2F05%2FGood-JOb-150x150.jpg&r=g', ''); + it('should not add missed extensions for other resources', () => { + const r = new Resource('http://1.gravatar.com/avatar/4d63e4a045c7ff22accc33dc08442f86?s=140&d=%2Fwp-content%2Fuploads%2F2015%2F05%2FGood-JOb-150x150.jpg&r=g', ''); r.getType = sinon.stub().returns('home'); - var filename = byTypeFilenameGenerator(r, {}, []); + const filename = byTypeFilenameGenerator(r, {}, []); filename.should.equalFileSystemPath('4d63e4a045c7ff22accc33dc08442f86'); }); - it('should return filename with correct subdirectory', function() { - var options = { + it('should return filename with correct subdirectory', () => { + const options = { subdirectories: [ { directory: 'img', extensions: ['.jpg', '.png', '.svg'] } ] }; - var r = new Resource('http://example.com/a.png'); - var filename = byTypeFilenameGenerator(r, options, []); + const r = new Resource('http://example.com/a.png'); + const filename = byTypeFilenameGenerator(r, options, []); filename.should.equalFileSystemPath('img/a.png'); }); - it('should return filename with correct subdirectory when string cases are different', function() { - var options = { - subdirectories: [ - { directory: 'img', extensions: ['.png'] } - ] - }; - - var r = new Resource('http://example.com/a.PNG'); - var f = byTypeFilenameGenerator(r, options, []); - f.should.equalFileSystemPath('img/a.PNG'); - }); - - it('should return different filename if desired filename is occupied', function() { - var r = new Resource('http://second-example.com/a.png'); - var filename = byTypeFilenameGenerator(r, {}, [ 'a.png' ]); + it('should return filename with correct subdirectory when string cases are different', () => { + const options = { + subdirectories: [ + { directory: 'img', extensions: ['.png'] } + ] + }; + + const r = new Resource('http://example.com/a.PNG'); + const f = byTypeFilenameGenerator(r, options, []); + f.should.equalFileSystemPath('img/a.PNG'); + }); + + it('should return different filename if desired filename is occupied', () => { + const r = new Resource('http://second-example.com/a.png'); + const filename = byTypeFilenameGenerator(r, {}, [ 'a.png' ]); filename.should.not.equalFileSystemPath('a.png'); }); - it('should return different filename if desired filename is occupied N times', function() { - var occupiedFilenames = []; - var r1 = new Resource('http://first-example.com/a.png'); - var r2 = new Resource('http://second-example.com/a.png'); - var r3 = new Resource('http://third-example.com/a.png'); - var r4 = new Resource('http://fourth-example.com/a.png'); + it('should return different filename if desired filename is occupied N times', () => { + const occupiedFilenames = []; + const r1 = new Resource('http://first-example.com/a.png'); + const r2 = new Resource('http://second-example.com/a.png'); + const r3 = new Resource('http://third-example.com/a.png'); + const r4 = new Resource('http://fourth-example.com/a.png'); - var f1 = byTypeFilenameGenerator(r1, {}, occupiedFilenames); + const f1 = byTypeFilenameGenerator(r1, {}, occupiedFilenames); f1.should.equalFileSystemPath('a.png'); occupiedFilenames.push(f1); - var f2 = byTypeFilenameGenerator(r2, {}, occupiedFilenames); + const f2 = byTypeFilenameGenerator(r2, {}, occupiedFilenames); f2.should.not.equal(f1); occupiedFilenames.push(f2); - var f3 = byTypeFilenameGenerator(r3, {}, occupiedFilenames); + const f3 = byTypeFilenameGenerator(r3, {}, occupiedFilenames); f3.should.not.equal(f1); f3.should.not.equal(f2); occupiedFilenames.push(f3); - var f4 = byTypeFilenameGenerator(r4, {}, occupiedFilenames); + const f4 = byTypeFilenameGenerator(r4, {}, occupiedFilenames); f4.should.not.equal(f1); f4.should.not.equal(f2); f4.should.not.equal(f3); }); - it('should shorten filename', function() { - var resourceFilename = _.repeat('a', 1000) + '.png'; - var r = new Resource('http://example.com/a.png', resourceFilename); - var filename = byTypeFilenameGenerator(r, {}, []); + it('should shorten filename', () => { + const resourceFilename = _.repeat('a', 1000) + '.png'; + const r = new Resource('http://example.com/a.png', resourceFilename); + const filename = byTypeFilenameGenerator(r, {}, []); should(filename.length).be.lessThan(255); }); - it('should return different short filename if first short filename is occupied', function() { - var resourceFilename = _.repeat('a', 1000) + '.png'; + it('should return different short filename if first short filename is occupied', () => { + const resourceFilename = _.repeat('a', 1000) + '.png'; - var r1 = new Resource('http://first-example.com/a.png', resourceFilename); - var r2 = new Resource('http://second-example.com/a.png', resourceFilename); + const r1 = new Resource('http://first-example.com/a.png', resourceFilename); + const r2 = new Resource('http://second-example.com/a.png', resourceFilename); - var f1 = byTypeFilenameGenerator(r1, {}, []); + const f1 = byTypeFilenameGenerator(r1, {}, []); should(f1.length).be.lessThan(255); - var f2 = byTypeFilenameGenerator(r2, {}, [ f1 ]); + const f2 = byTypeFilenameGenerator(r2, {}, [ f1 ]); should(f2.length).be.lessThan(255); should(f2).not.be.eql(f1); should(f2).not.be.eql(f1); }); - it('should return decoded url-based filename', function() { - var r = new Resource('https://developer.mozilla.org/ru/docs/JavaScript_%D1%88%D0%B5%D0%BB%D0%BB%D1%8B'); - var filename = byTypeFilenameGenerator(r, {}, []); + it('should return decoded url-based filename', () => { + const r = new Resource('https://developer.mozilla.org/ru/docs/JavaScript_%D1%88%D0%B5%D0%BB%D0%BB%D1%8B'); + const filename = byTypeFilenameGenerator(r, {}, []); filename.should.equalFileSystemPath('JavaScript_шеллы'); - var r2 = new Resource('https://developer.mozilla.org/Hello%20G%C3%BCnter.png'); - var filename2 = byTypeFilenameGenerator(r2, {}, []); + const r2 = new Resource('https://developer.mozilla.org/Hello%20G%C3%BCnter.png'); + const filename2 = byTypeFilenameGenerator(r2, {}, []); filename2.should.equalFileSystemPath('Hello Günter.png'); }); + + it('should remove not allowed characters from filename', () => { + const r1 = new Resource('http://example.com/some/path/<*a*>.png'); + byTypeFilenameGenerator(r1, {}, []).should.equalFileSystemPath('__a__.png'); + }); }); From 6ef295ac5228cd99592d6a1ada22b1c8874f1fd3 Mon Sep 17 00:00:00 2001 From: s0ph1e Date: Wed, 23 Sep 2020 21:50:09 +0300 Subject: [PATCH 2/2] Sanitize path --- lib/filename-generator/by-site-structure.js | 11 +++----- .../by-site-structure-test.js | 26 ++++++++++++------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/lib/filename-generator/by-site-structure.js b/lib/filename-generator/by-site-structure.js index a504c288..688f4138 100644 --- a/lib/filename-generator/by-site-structure.js +++ b/lib/filename-generator/by-site-structure.js @@ -13,7 +13,7 @@ module.exports = function generateFilename (resource, {defaultFilename}) { let filePath = utils.getFilepathFromUrl(resourceUrl); const extension = utils.getFilenameExtension(filePath); - filePath = path.join(host.replace(':', '_'), filePath); + filePath = path.join(host, filePath); // If have query string if (urlParsed.query) { @@ -50,12 +50,7 @@ module.exports = function generateFilename (resource, {defaultFilename}) { function sanitizeFilepath (filePath) { filePath = path.normalize(filePath); - let pathParts = filePath.split(path.sep); - pathParts = _.pull(pathParts, '..'); - pathParts[pathParts.length - 1] = getSanitizedFilename(_.last(pathParts)); + const pathParts = filePath.split(path.sep).map(pathPart => sanitizeFilename(pathPart, {replacement: '_'})); + pathParts[pathParts.length - 1] = utils.shortenFilename(_.last(pathParts)); return pathParts.join(path.sep); } - -function getSanitizedFilename (filename) { - return utils.shortenFilename(sanitizeFilename(filename, {replacement: '_'})); -} diff --git a/test/unit/filename-generator/by-site-structure-test.js b/test/unit/filename-generator/by-site-structure-test.js index 29945b17..bf160d93 100644 --- a/test/unit/filename-generator/by-site-structure-test.js +++ b/test/unit/filename-generator/by-site-structure-test.js @@ -14,9 +14,17 @@ describe('FilenameGenerator: bySiteStructure', () => { const r2 = new Resource('http://example.com/a.png'); bySiteStructureFilenameGenerator(r2, options).should.equalFileSystemPath('example.com/a.png'); + }); + + it('should remove . and .. from path', () => { + const r1 = new Resource('http://example.com/some/path/../images/a.png'); + bySiteStructureFilenameGenerator(r1, options).should.equalFileSystemPath('example.com/some/images/a.png'); - const r3 = new Resource('http://example.com/some/path/../images/a.png'); - bySiteStructureFilenameGenerator(r3, options).should.equalFileSystemPath('example.com/some/images/a.png'); + const r2 = new Resource('http://example.com/some/path/../../../images/b.png'); + bySiteStructureFilenameGenerator(r2, options).should.equalFileSystemPath('example.com/images/b.png'); + + const r3 = new Resource('http://example.com/some/path/./images/c.png'); + bySiteStructureFilenameGenerator(r3, options).should.equalFileSystemPath('example.com/some/path/images/c.png'); }); it('should replace the colon, for url with port number', () =>{ @@ -24,11 +32,16 @@ describe('FilenameGenerator: bySiteStructure', () => { bySiteStructureFilenameGenerator(r1, options).should.equalFileSystemPath('example.com_8080/some/path/a.png'); }); - it('should remove not allowed characters from filename', () => { + it('should replace not allowed characters from filename', () => { const r1 = new Resource('http://example.com/some/path/<*a*>.png'); bySiteStructureFilenameGenerator(r1, options).should.equalFileSystemPath('example.com/some/path/__a__.png'); }); + it('should replace not allowed characters from path', () => { + const r1 = new Resource('http://example.com/some:path/a.png'); + bySiteStructureFilenameGenerator(r1, options).should.equalFileSystemPath('example.com/some_path/a.png'); + }); + it('should add the defaultFilename to the path, for html resources without extension', () =>{ const isHtmlMock = sinon.stub().returns(true); @@ -58,13 +71,6 @@ describe('FilenameGenerator: bySiteStructure', () => { bySiteStructureFilenameGenerator(r, options).should.equalFileSystemPath('example.com/images/a.png'); }); - it('should not replace thrice dot in filenames', () => { - // if it replaces them we receive 'some/path/../../../../etc/passwd' - // path.resolve('some/path/../../../../etc/passwd'); = '/etc/passwd' => which is not safe - const r = new Resource('http://example.com/some/path/.../.../.../.../etc/passwd'); - bySiteStructureFilenameGenerator(r, options).should.equalFileSystemPath('example.com/some/path/.../.../.../.../etc/passwd'); - }); - it('should shorten filename', () => { const resourceFilename = _.repeat('1', 1000) + '.png'; const r = new Resource('http://example.com/' + resourceFilename);