Merge pull request from GHSA-6c37-r62q-7xf4

This commit is contained in:
Shaun Hamilton
2023-07-18 12:27:45 +02:00
committed by GitHub
parent 8804f8be93
commit 414987ae2c
2 changed files with 148 additions and 5 deletions

View File

@@ -234,11 +234,43 @@ const updatePrivacyTerms = (req, res, next) => {
);
};
function updateMySocials(...args) {
const buildUpdate = body =>
_.pick(body, ['githubProfile', 'linkedin', 'twitter', 'website']);
const validate = update =>
Object.values(update).every(x => typeof x === 'string');
const allowedSocialsAndDomains = {
githubProfile: 'github.com',
linkedin: 'linkedin.com',
twitter: 'twitter.com',
website: ''
};
const socialVals = Object.keys(allowedSocialsAndDomains);
export function updateMySocials(...args) {
const buildUpdate = body => _.pick(body, socialVals);
const validate = update => {
// Socials should point to their respective domains
// or be empty strings
return Object.keys(update).every(key => {
const val = update[key];
if (val === '') {
return true;
}
if (key === 'website') {
return isURL(val, { require_protocol: true });
}
const domain = allowedSocialsAndDomains[key];
try {
const url = new URL(val);
const topDomain = url.hostname.split('.').slice(-2);
if (topDomain.length === 2) {
return topDomain.join('.') === domain;
}
return false;
} catch (e) {
return false;
}
});
};
createUpdateUserProperties(
buildUpdate,
validate,

View File

@@ -0,0 +1,111 @@
import { updateMySocials } from '../boot/settings';
export const mockReq = opts => {
const req = {};
return { ...req, ...opts };
};
export const mockRes = opts => {
const res = {};
res.status = jest.fn().mockReturnValue(res);
res.json = jest.fn().mockReturnValue(res);
res.redirect = jest.fn().mockReturnValue(res);
res.set = jest.fn().mockReturnValue(res);
res.clearCookie = jest.fn().mockReturnValue(res);
res.cookie = jest.fn().mockReturnValue(res);
return { ...res, ...opts };
};
describe('boot/settings', () => {
describe('updateMySocials', () => {
it('does not allow non-github domain in GitHub social', () => {
const req = mockReq({
user: {},
body: {
githubProfile: 'https://www.almost-github.com'
}
});
const res = mockRes();
const next = jest.fn();
updateMySocials(req, res, next);
expect(res.status).toHaveBeenCalledWith(403);
});
it('does not allow non-linkedin domain in LinkedIn social', () => {
const req = mockReq({
user: {},
body: {
linkedin: 'https://www.freecodecamp.org'
}
});
const res = mockRes();
const next = jest.fn();
updateMySocials(req, res, next);
expect(res.status).toHaveBeenCalledWith(403);
});
it('does not allow non-twitter domain in Twitter social', () => {
const req = mockReq({
user: {},
body: {
twitter: 'https://www.freecodecamp.org'
}
});
const res = mockRes();
const next = jest.fn();
updateMySocials(req, res, next);
expect(res.status).toHaveBeenCalledWith(403);
});
it('allows empty string in any social', () => {
const req = mockReq({
user: {
updateAttributes: (_, cb) => cb()
},
body: {
twitter: '',
linkedin: '',
githubProfile: '',
website: ''
}
});
const res = mockRes();
const next = jest.fn();
updateMySocials(req, res, next);
expect(res.status).toHaveBeenCalledWith(200);
});
it('allows any valid link in website social', () => {
const req = mockReq({
user: {
updateAttributes: (_, cb) => cb()
},
body: {
website: 'https://www.freecodecamp.org'
}
});
const res = mockRes();
const next = jest.fn();
updateMySocials(req, res, next);
expect(res.status).toHaveBeenCalledWith(200);
});
it('allows valid links with sub-domains to pass', () => {
const req = mockReq({
user: {
updateAttributes: (_, cb) => cb()
},
body: {
githubProfile: 'https://www.gist.github.com',
linkedin: 'https://www.linkedin.com/freecodecamp',
twitter: 'https://www.twitter.com/freecodecamp',
website: 'https://www.example.freecodecamp.org'
}
});
const res = mockRes();
const next = jest.fn();
updateMySocials(req, res, next);
expect(res.status).toHaveBeenCalledWith(200);
});
});
});