Skip to content

Commit 9cf3c4a

Browse files
author
Mike Patnode
committed
Move filename validation out of the Router and into the FilesAdaptor
1 parent f67f8db commit 9cf3c4a

File tree

6 files changed

+61
-19
lines changed

6 files changed

+61
-19
lines changed

spec/AdaptableController.spec.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ describe('AdaptableController', () => {
6464
deleteFile: function() {},
6565
getFileData: function() {},
6666
getFileLocation: function() {},
67+
validateFilename: function() {},
6768
};
6869
expect(() => {
6970
new FilesController(adapter);
@@ -77,6 +78,7 @@ describe('AdaptableController', () => {
7778
AGoodAdapter.prototype.deleteFile = function() {};
7879
AGoodAdapter.prototype.getFileData = function() {};
7980
AGoodAdapter.prototype.getFileLocation = function() {};
81+
AGoodAdapter.prototype.validateFilename = function() {};
8082

8183
const adapter = new AGoodAdapter();
8284
expect(() => {

spec/FilesController.spec.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const mockAdapter = {
1414
deleteFile: () => {},
1515
getFileData: () => {},
1616
getFileLocation: () => 'xyz',
17+
validateFilename: () => {},
1718
};
1819

1920
// Small additional tests to improve overall coverage
@@ -118,4 +119,13 @@ describe('FilesController', () => {
118119

119120
done();
120121
});
122+
123+
it('should reject slashes in file names', done => {
124+
const gridStoreAdapter = new GridFSBucketAdapter(
125+
'mongodb://localhost:27017/parse'
126+
);
127+
const fileName = 'foo/randomFileName.pdf';
128+
expect(gridStoreAdapter.validateFilename(fileName)).not.toBe(null);
129+
done();
130+
});
121131
});

src/Adapters/Files/FilesAdapter.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
// * deleteFile(filename)
99
// * getFileData(filename)
1010
// * getFileLocation(config, filename)
11+
// * validateFilename(filename)
1112
//
1213
// Default is GridFSBucketAdapter, which requires mongo
1314
// and for the API server to be using the DatabaseController with Mongo
1415
// database adapter.
1516

1617
import type { Config } from '../../Config';
18+
import Parse from 'parse/lib/node/Parse';
1719
/**
1820
* @module Adapters
1921
*/
@@ -56,6 +58,30 @@ export class FilesAdapter {
5658
* @return {string} Absolute URL
5759
*/
5860
getFileLocation(config: Config, filename: string): string {}
61+
62+
/**
63+
*
64+
* @param {string} filename
65+
*
66+
* @returns {null|*|Parse.Error} null if there are no errors
67+
*/
68+
validateFilename(filename): ?Parse.Error {}
69+
}
70+
71+
export function validateFilename(filename): ?Parse.Error {
72+
if (filename.length > 128) {
73+
return new Parse.Error(Parse.Error.INVALID_FILE_NAME, 'Filename too long.');
74+
}
75+
76+
// US/ASCII centric default
77+
const regx = /^[_a-zA-Z0-9][a-zA-Z0-9@. ~_-]*$/;
78+
if (!filename.match(regx)) {
79+
return new Parse.Error(
80+
Parse.Error.INVALID_FILE_NAME,
81+
'Filename contains invalid characters.'
82+
);
83+
}
84+
return null; // No errors
5985
}
6086

6187
export default FilesAdapter;

src/Adapters/Files/GridFSBucketAdapter.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
// @flow-disable-next
1010
import { MongoClient, GridFSBucket, Db } from 'mongodb';
11-
import { FilesAdapter } from './FilesAdapter';
11+
import { FilesAdapter, validateFilename } from './FilesAdapter';
1212
import defaults from '../../defaults';
1313

1414
export class GridFSBucketAdapter extends FilesAdapter {
@@ -139,6 +139,10 @@ export class GridFSBucketAdapter extends FilesAdapter {
139139
}
140140
return this._client.close(false);
141141
}
142+
143+
validateFilename(filename) {
144+
return validateFilename(filename);
145+
}
142146
}
143147

144148
export default GridFSBucketAdapter;

src/Adapters/Files/GridStoreAdapter.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
// @flow-disable-next
1111
import { MongoClient, GridStore, Db } from 'mongodb';
12-
import { FilesAdapter } from './FilesAdapter';
12+
import { FilesAdapter, validateFilename } from './FilesAdapter';
1313
import defaults from '../../defaults';
1414

1515
export class GridStoreAdapter extends FilesAdapter {
@@ -110,6 +110,10 @@ export class GridStoreAdapter extends FilesAdapter {
110110
}
111111
return this._client.close(false);
112112
}
113+
114+
validateFilename(filename) {
115+
return validateFilename(filename);
116+
}
113117
}
114118

115119
// handleRangeRequest is licensed under Creative Commons Attribution 4.0 International License (https://creativecommons.org/licenses/by/4.0/).

src/Routers/FilesRouter.js

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Parse from 'parse/node';
55
import Config from '../Config';
66
import mime from 'mime';
77
import logger from '../logger';
8+
import validateFilename from '../Adapters/Files/FilesAdapter';
89

910
export class FilesRouter {
1011
expressRouter({ maxUploadSize = '20Mb' } = {}) {
@@ -69,35 +70,30 @@ export class FilesRouter {
6970
}
7071

7172
createHandler(req, res, next) {
73+
const config = req.config;
74+
const filesController = config.filesController;
75+
const filename = req.params.filename;
76+
const contentType = req.get('Content-type');
77+
7278
if (!req.body || !req.body.length) {
7379
next(
7480
new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'Invalid file upload.')
7581
);
7682
return;
7783
}
7884

79-
if (req.params.filename.length > 128) {
80-
next(
81-
new Parse.Error(Parse.Error.INVALID_FILE_NAME, 'Filename too long.')
82-
);
83-
return;
85+
var error;
86+
if (typeof filesController.adapter.validateFilename === 'function') {
87+
error = filesController.adapter.validateFilename(filename);
88+
} else {
89+
error = validateFilename(filename);
8490
}
8591

86-
if (!req.params.filename.match(/^[_a-zA-Z0-9][a-zA-Z0-9@\.\ ~_-]*$/)) {
87-
next(
88-
new Parse.Error(
89-
Parse.Error.INVALID_FILE_NAME,
90-
'Filename contains invalid characters.'
91-
)
92-
);
92+
if (error) {
93+
next(error);
9394
return;
9495
}
9596

96-
const filename = req.params.filename;
97-
const contentType = req.get('Content-type');
98-
const config = req.config;
99-
const filesController = config.filesController;
100-
10197
filesController
10298
.createFile(config, filename, req.body, contentType)
10399
.then(result => {

0 commit comments

Comments
 (0)