Skip to content

Commit

Permalink
fix: creating a Parse.File with base64 string fails for some file typ…
Browse files Browse the repository at this point in the history
…es (#1467)
  • Loading branch information
RazvanCristian authored May 2, 2022
1 parent b90ae25 commit c07d6c9
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 21 deletions.
2 changes: 2 additions & 0 deletions integration/test/ParseFileTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ describe('Parse.File', () => {

it('can get file data from base64', async () => {
const file = new Parse.File('parse-server-logo', { base64: 'ParseA==' });
await file.save();
let data = await file.getData();
assert.equal(data, 'ParseA==');
file._data = null;
Expand All @@ -79,6 +80,7 @@ describe('Parse.File', () => {
const file = new Parse.File('parse-server-logo', {
base64: 'data:image/jpeg;base64,ParseA==',
});
await file.save();
let data = await file.getData();
assert.equal(data, 'ParseA==');
file._data = null;
Expand Down
52 changes: 32 additions & 20 deletions src/ParseFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,15 @@ export type FileSource =
type: string,
};

const dataUriRegexp = /^data:([a-zA-Z]+\/[-a-zA-Z0-9+.]+)(;charset=[a-zA-Z0-9\-\/]*)?;base64,/;
const base64Regex = new RegExp(
'([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))',
'i'
);

const dataUriRegex = new RegExp(
`^data:([a-zA-Z]+\\/[-a-zA-Z0-9+.]+(;[a-z-]+=[a-zA-Z0-9+.-]+)?)?(;base64)?,(${base64Regex.source})*$`,
'i'
);

function b64Digit(number: number): string {
if (number < 26) {
Expand Down Expand Up @@ -137,26 +145,30 @@ class ParseFile {
type: specifiedType,
};
} else if (data && typeof data.base64 === 'string') {
const base64 = data.base64;
const commaIndex = base64.indexOf(',');

if (commaIndex !== -1) {
const matches = dataUriRegexp.exec(base64.slice(0, commaIndex + 1));
// if data URI with type and charset, there will be 4 matches.
this._data = base64.slice(commaIndex + 1);
this._source = {
format: 'base64',
base64: this._data,
type: matches[1],
};
} else {
this._data = base64;
this._source = {
format: 'base64',
base64: base64,
type: specifiedType,
};
// Check if data URI or base64 string is valid
const validationRegex = new RegExp(base64Regex.source + '|' + dataUriRegex.source, 'i');
if (!validationRegex.test(data.base64)) {
throw new Error(
'Cannot create a Parse.File without valid data URIs or base64 encoded data.'
);
}

const base64 = data.base64.split(',').slice(-1)[0];
let type =
specifiedType || data.base64.split(';').slice(0, 1)[0].split(':').slice(1, 2)[0] || '';

// https://tools.ietf.org/html/rfc2397
// If <mediatype> is omitted, it defaults to text/plain;charset=US-ASCII.
// As a shorthand, "text/plain" can be omitted but the charset parameter supplied.
if (dataUriRegex.test(data.base64)) {
type = type || 'text/plain';
}

this._source = {
format: 'base64',
base64,
type,
};
} else {
throw new TypeError('Cannot create a Parse.File with that data.');
}
Expand Down
24 changes: 23 additions & 1 deletion src/__tests__/ParseFile-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,30 @@ describe('ParseFile', () => {
expect(file._source.type).toBe('');
});

it('can extract data type from base64', () => {
it('can set the default type to be text/plain when using base64', () => {
const file = new ParseFile('parse.txt', {
base64: 'data:;base64,ParseA==',
});
expect(file._source.base64).toBe('ParseA==');
expect(file._source.type).toBe('text/plain');
});

it('can extract data type from base64', () => {
const file = new ParseFile('parse.png', {
base64: 'data:image/png;base64,ParseA==',
});
expect(file._source.base64).toBe('ParseA==');
expect(file._source.type).toBe('image/png');
});

it('can extract data type from base64 with a filename parameter', () => {
const file = new ParseFile('parse.pdf', {
base64: 'data:application/pdf;filename=parse.pdf;base64,ParseA==',
});
expect(file._source.base64).toBe('ParseA==');
expect(file._source.type).toBe('application/pdf');
});

it('can create files with file uri', () => {
const file = new ParseFile('parse-image', {
uri: 'http://example.com/image.png',
Expand Down Expand Up @@ -136,6 +152,12 @@ describe('ParseFile', () => {
expect(function () {
new ParseFile('parse.txt', 'string');
}).toThrow('Cannot create a Parse.File with that data.');

expect(function () {
new ParseFile('parse.txt', {
base64: 'abc',
});
}).toThrow('Cannot create a Parse.File without valid data URIs or base64 encoded data.');
});

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

0 comments on commit c07d6c9

Please sign in to comment.