Skip to content

Commit c07d6c9

Browse files
fix: creating a Parse.File with base64 string fails for some file types (#1467)
1 parent b90ae25 commit c07d6c9

File tree

3 files changed

+57
-21
lines changed

3 files changed

+57
-21
lines changed

integration/test/ParseFileTest.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ describe('Parse.File', () => {
6666

6767
it('can get file data from base64', async () => {
6868
const file = new Parse.File('parse-server-logo', { base64: 'ParseA==' });
69+
await file.save();
6970
let data = await file.getData();
7071
assert.equal(data, 'ParseA==');
7172
file._data = null;
@@ -79,6 +80,7 @@ describe('Parse.File', () => {
7980
const file = new Parse.File('parse-server-logo', {
8081
base64: 'data:image/jpeg;base64,ParseA==',
8182
});
83+
await file.save();
8284
let data = await file.getData();
8385
assert.equal(data, 'ParseA==');
8486
file._data = null;

src/ParseFile.js

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,15 @@ export type FileSource =
4242
type: string,
4343
};
4444

45-
const dataUriRegexp = /^data:([a-zA-Z]+\/[-a-zA-Z0-9+.]+)(;charset=[a-zA-Z0-9\-\/]*)?;base64,/;
45+
const base64Regex = new RegExp(
46+
'([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))',
47+
'i'
48+
);
49+
50+
const dataUriRegex = new RegExp(
51+
`^data:([a-zA-Z]+\\/[-a-zA-Z0-9+.]+(;[a-z-]+=[a-zA-Z0-9+.-]+)?)?(;base64)?,(${base64Regex.source})*$`,
52+
'i'
53+
);
4654

4755
function b64Digit(number: number): string {
4856
if (number < 26) {
@@ -137,26 +145,30 @@ class ParseFile {
137145
type: specifiedType,
138146
};
139147
} else if (data && typeof data.base64 === 'string') {
140-
const base64 = data.base64;
141-
const commaIndex = base64.indexOf(',');
142-
143-
if (commaIndex !== -1) {
144-
const matches = dataUriRegexp.exec(base64.slice(0, commaIndex + 1));
145-
// if data URI with type and charset, there will be 4 matches.
146-
this._data = base64.slice(commaIndex + 1);
147-
this._source = {
148-
format: 'base64',
149-
base64: this._data,
150-
type: matches[1],
151-
};
152-
} else {
153-
this._data = base64;
154-
this._source = {
155-
format: 'base64',
156-
base64: base64,
157-
type: specifiedType,
158-
};
148+
// Check if data URI or base64 string is valid
149+
const validationRegex = new RegExp(base64Regex.source + '|' + dataUriRegex.source, 'i');
150+
if (!validationRegex.test(data.base64)) {
151+
throw new Error(
152+
'Cannot create a Parse.File without valid data URIs or base64 encoded data.'
153+
);
154+
}
155+
156+
const base64 = data.base64.split(',').slice(-1)[0];
157+
let type =
158+
specifiedType || data.base64.split(';').slice(0, 1)[0].split(':').slice(1, 2)[0] || '';
159+
160+
// https://tools.ietf.org/html/rfc2397
161+
// If <mediatype> is omitted, it defaults to text/plain;charset=US-ASCII.
162+
// As a shorthand, "text/plain" can be omitted but the charset parameter supplied.
163+
if (dataUriRegex.test(data.base64)) {
164+
type = type || 'text/plain';
159165
}
166+
167+
this._source = {
168+
format: 'base64',
169+
base64,
170+
type,
171+
};
160172
} else {
161173
throw new TypeError('Cannot create a Parse.File with that data.');
162174
}

src/__tests__/ParseFile-test.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,30 @@ describe('ParseFile', () => {
6666
expect(file._source.type).toBe('');
6767
});
6868

69-
it('can extract data type from base64', () => {
69+
it('can set the default type to be text/plain when using base64', () => {
7070
const file = new ParseFile('parse.txt', {
71+
base64: 'data:;base64,ParseA==',
72+
});
73+
expect(file._source.base64).toBe('ParseA==');
74+
expect(file._source.type).toBe('text/plain');
75+
});
76+
77+
it('can extract data type from base64', () => {
78+
const file = new ParseFile('parse.png', {
7179
base64: 'data:image/png;base64,ParseA==',
7280
});
7381
expect(file._source.base64).toBe('ParseA==');
7482
expect(file._source.type).toBe('image/png');
7583
});
7684

85+
it('can extract data type from base64 with a filename parameter', () => {
86+
const file = new ParseFile('parse.pdf', {
87+
base64: 'data:application/pdf;filename=parse.pdf;base64,ParseA==',
88+
});
89+
expect(file._source.base64).toBe('ParseA==');
90+
expect(file._source.type).toBe('application/pdf');
91+
});
92+
7793
it('can create files with file uri', () => {
7894
const file = new ParseFile('parse-image', {
7995
uri: 'http://example.com/image.png',
@@ -136,6 +152,12 @@ describe('ParseFile', () => {
136152
expect(function () {
137153
new ParseFile('parse.txt', 'string');
138154
}).toThrow('Cannot create a Parse.File with that data.');
155+
156+
expect(function () {
157+
new ParseFile('parse.txt', {
158+
base64: 'abc',
159+
});
160+
}).toThrow('Cannot create a Parse.File without valid data URIs or base64 encoded data.');
139161
});
140162

141163
it('throws with invalid base64', () => {

0 commit comments

Comments
 (0)