BREAKING CHANGE: Fields in the internal scope of Parse Server (prefixed with underscore `_`) are only returned using the new `maintenanceKey`; previously the `masterKey` allowed reading of internal fields; see [access scopes](https://github.com/parse-community/parse-server#access-scopes) for a comparison of the keys' access permissions (#8212)
211 lines
6.6 KiB
JavaScript
211 lines
6.6 KiB
JavaScript
const request = require('../lib/request');
|
|
|
|
const serverURL = 'http://localhost:8378/1';
|
|
const headers = {
|
|
'Content-Type': 'application/json',
|
|
};
|
|
const keys = {
|
|
_ApplicationId: 'test',
|
|
_JavaScriptKey: 'test',
|
|
};
|
|
const emailAdapter = {
|
|
sendVerificationEmail: () => Promise.resolve(),
|
|
sendPasswordResetEmail: () => Promise.resolve(),
|
|
sendMail: () => {},
|
|
};
|
|
const appName = 'test';
|
|
const publicServerURL = 'http://localhost:8378/1';
|
|
|
|
describe('Regex Vulnerabilities', function () {
|
|
beforeEach(async function () {
|
|
await reconfigureServer({
|
|
maintenanceKey: 'test2',
|
|
verifyUserEmails: true,
|
|
emailAdapter,
|
|
appName,
|
|
publicServerURL,
|
|
});
|
|
|
|
const signUpResponse = await request({
|
|
url: `${serverURL}/users`,
|
|
method: 'POST',
|
|
headers,
|
|
body: JSON.stringify({
|
|
...keys,
|
|
_method: 'POST',
|
|
username: 'someemail@somedomain.com',
|
|
password: 'somepassword',
|
|
email: 'someemail@somedomain.com',
|
|
}),
|
|
});
|
|
this.objectId = signUpResponse.data.objectId;
|
|
this.sessionToken = signUpResponse.data.sessionToken;
|
|
this.partialSessionToken = this.sessionToken.slice(0, 3);
|
|
});
|
|
|
|
describe('on session token', function () {
|
|
it('should not work with regex', async function () {
|
|
try {
|
|
await request({
|
|
url: `${serverURL}/users/me`,
|
|
method: 'POST',
|
|
headers,
|
|
body: JSON.stringify({
|
|
...keys,
|
|
_SessionToken: {
|
|
$regex: this.partialSessionToken,
|
|
},
|
|
_method: 'GET',
|
|
}),
|
|
});
|
|
fail('should not work');
|
|
} catch (e) {
|
|
expect(e.data.code).toEqual(209);
|
|
expect(e.data.error).toEqual('Invalid session token');
|
|
}
|
|
});
|
|
|
|
it('should work with plain token', async function () {
|
|
const meResponse = await request({
|
|
url: `${serverURL}/users/me`,
|
|
method: 'POST',
|
|
headers,
|
|
body: JSON.stringify({
|
|
...keys,
|
|
_SessionToken: this.sessionToken,
|
|
_method: 'GET',
|
|
}),
|
|
});
|
|
expect(meResponse.data.objectId).toEqual(this.objectId);
|
|
expect(meResponse.data.sessionToken).toEqual(this.sessionToken);
|
|
});
|
|
});
|
|
|
|
describe('on verify e-mail', function () {
|
|
beforeEach(async function () {
|
|
const userQuery = new Parse.Query(Parse.User);
|
|
this.user = await userQuery.get(this.objectId, { useMasterKey: true });
|
|
});
|
|
|
|
it('should not work with regex', async function () {
|
|
expect(this.user.get('emailVerified')).toEqual(false);
|
|
await request({
|
|
url: `${serverURL}/apps/test/verify_email?username=someemail@somedomain.com&token[$regex]=`,
|
|
method: 'GET',
|
|
});
|
|
await this.user.fetch({ useMasterKey: true });
|
|
expect(this.user.get('emailVerified')).toEqual(false);
|
|
});
|
|
|
|
it('should work with plain token', async function () {
|
|
expect(this.user.get('emailVerified')).toEqual(false);
|
|
const current = await request({
|
|
method: 'GET',
|
|
url: `http://localhost:8378/1/classes/_User/${this.user.id}`,
|
|
json: true,
|
|
headers: {
|
|
'X-Parse-Application-Id': 'test',
|
|
'X-Parse-Rest-API-Key': 'test',
|
|
'X-Parse-Maintenance-Key': 'test2',
|
|
'Content-Type': 'application/json',
|
|
},
|
|
}).then(res => res.data);
|
|
// It should work
|
|
await request({
|
|
url: `${serverURL}/apps/test/verify_email?username=someemail@somedomain.com&token=${current._email_verify_token}`,
|
|
method: 'GET',
|
|
});
|
|
await this.user.fetch({ useMasterKey: true });
|
|
expect(this.user.get('emailVerified')).toEqual(true);
|
|
});
|
|
});
|
|
|
|
describe('on password reset', function () {
|
|
beforeEach(async function () {
|
|
this.user = await Parse.User.logIn('someemail@somedomain.com', 'somepassword');
|
|
});
|
|
|
|
it('should not work with regex', async function () {
|
|
expect(this.user.id).toEqual(this.objectId);
|
|
await request({
|
|
url: `${serverURL}/requestPasswordReset`,
|
|
method: 'POST',
|
|
headers,
|
|
body: JSON.stringify({
|
|
...keys,
|
|
_method: 'POST',
|
|
email: 'someemail@somedomain.com',
|
|
}),
|
|
});
|
|
await this.user.fetch({ useMasterKey: true });
|
|
const passwordResetResponse = await request({
|
|
url: `${serverURL}/apps/test/request_password_reset?username=someemail@somedomain.com&token[$regex]=`,
|
|
method: 'GET',
|
|
});
|
|
expect(passwordResetResponse.status).toEqual(302);
|
|
expect(passwordResetResponse.headers.location).toMatch(`\\/invalid\\_link\\.html`);
|
|
await request({
|
|
url: `${serverURL}/apps/test/request_password_reset`,
|
|
method: 'POST',
|
|
body: {
|
|
token: { $regex: '' },
|
|
username: 'someemail@somedomain.com',
|
|
new_password: 'newpassword',
|
|
},
|
|
});
|
|
try {
|
|
await Parse.User.logIn('someemail@somedomain.com', 'newpassword');
|
|
fail('should not work');
|
|
} catch (e) {
|
|
expect(e.code).toEqual(Parse.Error.OBJECT_NOT_FOUND);
|
|
expect(e.message).toEqual('Invalid username/password.');
|
|
}
|
|
});
|
|
|
|
it('should work with plain token', async function () {
|
|
expect(this.user.id).toEqual(this.objectId);
|
|
await request({
|
|
url: `${serverURL}/requestPasswordReset`,
|
|
method: 'POST',
|
|
headers,
|
|
body: JSON.stringify({
|
|
...keys,
|
|
_method: 'POST',
|
|
email: 'someemail@somedomain.com',
|
|
}),
|
|
});
|
|
const current = await request({
|
|
method: 'GET',
|
|
url: `http://localhost:8378/1/classes/_User/${this.user.id}`,
|
|
json: true,
|
|
headers: {
|
|
'X-Parse-Application-Id': 'test',
|
|
'X-Parse-Rest-API-Key': 'test',
|
|
'X-Parse-Maintenance-Key': 'test2',
|
|
'Content-Type': 'application/json',
|
|
},
|
|
}).then(res => res.data);
|
|
const token = current._perishable_token;
|
|
const passwordResetResponse = await request({
|
|
url: `${serverURL}/apps/test/request_password_reset?username=someemail@somedomain.com&token=${token}`,
|
|
method: 'GET',
|
|
});
|
|
expect(passwordResetResponse.status).toEqual(302);
|
|
expect(passwordResetResponse.headers.location).toMatch(
|
|
`\\/choose\\_password\\?token\\=${token}\\&`
|
|
);
|
|
await request({
|
|
url: `${serverURL}/apps/test/request_password_reset`,
|
|
method: 'POST',
|
|
body: {
|
|
token,
|
|
username: 'someemail@somedomain.com',
|
|
new_password: 'newpassword',
|
|
},
|
|
});
|
|
const userAgain = await Parse.User.logIn('someemail@somedomain.com', 'newpassword');
|
|
expect(userAgain.id).toEqual(this.objectId);
|
|
});
|
|
});
|
|
});
|