Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions spec/vulnerabilities.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3750,4 +3750,97 @@ describe('(GHSA-qpc3-fg4j-8hgm) Protected field change detection oracle via Live
expect(updateSpy).not.toHaveBeenCalled();
});

describe('(GHSA-8pjv-59c8-44p8) SSRF via Webhook URL requires master key', () => {
const expectMasterKeyRequired = async promise => {
try {
await promise;
fail('Expected request to be rejected');
} catch (error) {
expect(error.status).toBe(403);
}
};

it('rejects registering a webhook function with internal URL without master key', async () => {
await expectMasterKeyRequired(
request({
method: 'POST',
url: Parse.serverURL + '/hooks/functions',
headers: {
'X-Parse-Application-Id': Parse.applicationId,
'X-Parse-REST-API-Key': 'rest',
},
body: JSON.stringify({
functionName: 'ssrf_probe',
url: 'http://169.254.169.254/latest/meta-data/iam/security-credentials/',
}),
})
);
});

it('rejects updating a webhook function URL to internal address without master key', async () => {
// Seed a legitimate webhook first so the PUT hits auth, not "not found"
await request({
method: 'POST',
url: Parse.serverURL + '/hooks/functions',
headers: {
'X-Parse-Application-Id': Parse.applicationId,
'X-Parse-Master-Key': Parse.masterKey,
'Content-Type': 'application/json',
},
body: JSON.stringify({
functionName: 'ssrf_probe',
url: 'https://example.com/webhook',
}),
});
await expectMasterKeyRequired(
request({
method: 'PUT',
url: Parse.serverURL + '/hooks/functions/ssrf_probe',
headers: {
'X-Parse-Application-Id': Parse.applicationId,
'X-Parse-REST-API-Key': 'rest',
},
body: JSON.stringify({
url: 'http://169.254.169.254/latest/meta-data/',
}),
})
);
});

it('rejects registering a webhook trigger with internal URL without master key', async () => {
await expectMasterKeyRequired(
request({
method: 'POST',
url: Parse.serverURL + '/hooks/triggers',
headers: {
'X-Parse-Application-Id': Parse.applicationId,
'X-Parse-REST-API-Key': 'rest',
},
body: JSON.stringify({
className: 'TestClass',
triggerName: 'beforeSave',
url: 'http://127.0.0.1:8080/admin/status',
}),
})
);
});

it('rejects registering a webhook with internal URL using JavaScript key', async () => {
await expectMasterKeyRequired(
request({
method: 'POST',
url: Parse.serverURL + '/hooks/functions',
headers: {
'X-Parse-Application-Id': Parse.applicationId,
'X-Parse-JavaScript-Key': 'test',
},
body: JSON.stringify({
functionName: 'ssrf_probe',
url: 'http://10.0.0.1:3000/internal-api',
}),
})
);
});
});

});
Loading