Merge pull request from GHSA-cc3r-grh4-27gj

* feat: restrict update-user-flag endpoint

* feat(api): add dedicated-endpoints for user

Co-authored-by: Naomi Carrigan <nhcarrigan@gmail.com>
Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
Mrugesh Mohapatra
2022-04-13 20:51:31 +05:30
committed by GitHub
parent bb0f9c8036
commit 9ea7018740
3 changed files with 97 additions and 16 deletions

View File

@ -1,5 +1,6 @@
import debug from 'debug';
import { check } from 'express-validator';
import _ from 'lodash';
import isURL from 'validator/lib/isURL';
import { isValidUsername } from '../../../../utils/validate';
@ -24,8 +25,8 @@ export default function settingsController(app) {
createValidatorErrorHandler(alertTypes.danger),
updateMyCurrentChallenge
);
api.post('/update-my-portfolio', ifNoUser401, updateMyPortfolio);
api.post('/update-my-theme', deprecatedEndpoint);
api.put('/update-my-portfolio', ifNoUser401, updateMyPortfolio);
api.put('/update-my-theme', ifNoUser401, updateMyTheme);
api.put('/update-my-about', ifNoUser401, updateMyAbout);
api.put(
'/update-my-email',
@ -37,6 +38,10 @@ export default function settingsController(app) {
api.put('/update-my-profileui', ifNoUser401, updateMyProfileUI);
api.put('/update-my-username', ifNoUser401, updateMyUsername);
api.put('/update-user-flag', ifNoUser401, updateUserFlag);
api.put('/update-my-socials', ifNoUser401, updateMySocials);
api.put('/update-my-sound', ifNoUser401, updateMySound);
api.put('/update-my-honesty', ifNoUser401, updateMyHonesty);
api.put('/update-my-quincy-email', ifNoUser401, updateMyQuincyEmail);
app.use(api);
}
@ -97,18 +102,16 @@ function updateMyCurrentChallenge(req, res, next) {
);
}
function updateMyPortfolio(req, res, next) {
const {
user,
body: { portfolio }
} = req;
// if we only have one key, it should be the id
// user cannot send only one key to this route
// other than to remove a portfolio item
const requestDelete = Object.keys(portfolio).length === 1;
return user
.updateMyPortfolio(portfolio, requestDelete)
.subscribe(message => res.json({ message }), next);
function updateMyPortfolio(...args) {
const portfolioKeys = ['id', 'title', 'description', 'url', 'image'];
const buildUpdate = body => {
const portfolio = body?.portfolio?.map(elem => _.pick(elem, portfolioKeys));
return { portfolio };
};
const validate = ({ portfolio }) => portfolio?.every(isPortfolioElement);
const isPortfolioElement = elem =>
Object.values(elem).every(val => typeof val == 'string');
createUpdateUserProperties(buildUpdate, validate)(...args);
}
function updateMyProfileUI(req, res, next) {
@ -204,7 +207,80 @@ 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');
createUpdateUserProperties(buildUpdate, validate)(...args);
}
function updateMyTheme(...args) {
const buildUpdate = body => _.pick(body, 'theme');
const validate = ({ theme }) => theme == 'default' || theme == 'night';
createUpdateUserProperties(buildUpdate, validate)(...args);
}
function updateMySound(...args) {
const buildUpdate = body => _.pick(body, 'sound');
const validate = ({ sound }) => typeof sound === 'boolean';
createUpdateUserProperties(buildUpdate, validate)(...args);
}
function updateMyHonesty(...args) {
const buildUpdate = body => _.pick(body, 'isHonest');
const validate = ({ isHonest }) => isHonest === true;
createUpdateUserProperties(buildUpdate, validate)(...args);
}
function updateMyQuincyEmail(...args) {
const buildUpdate = body => _.pick(body, 'sendQuincyEmail');
const validate = ({ sendQuincyEmail }) =>
typeof sendQuincyEmail === 'boolean';
createUpdateUserProperties(buildUpdate, validate)(...args);
}
function createUpdateUserProperties(buildUpdate, validate) {
return (req, res, next) => {
const { user, body } = req;
const update = buildUpdate(body);
if (validate(update)) {
user.updateAttributes(update, createStandardHandler(req, res, next));
} else {
handleInvalidUpdate(res);
}
};
}
function handleInvalidUpdate(res) {
res.status(403).json({
type: 'danger',
message: 'flash.wrong-updating'
});
}
function updateUserFlag(req, res, next) {
const { user, body: update } = req;
return user.updateAttributes(update, createStandardHandler(req, res, next));
const allowedKeys = [
'theme',
'sound',
'isHonest',
'portfolio',
'sendQuincyEmail',
'isGithub',
'isLinkedIn',
'isTwitter',
'isWebsite',
'githubProfile',
'linkedin',
'twitter',
'website'
];
if (Object.keys(update).every(key => allowedKeys.includes(key))) {
return user.updateAttributes(update, createStandardHandler(req, res, next));
}
return res.status(403).json({
type: 'danger',
message: 'flash.invalid-update-flag'
});
}

View File

@ -536,7 +536,8 @@
"code-save-error": "An error occurred trying to save your code.",
"code-save-less": "Slow Down! Your code was not saved. Try again in a few seconds.",
"challenge-save-too-big": "Sorry, you cannot save your code. Your code is {{user-size}} bytes. We allow a maximum of {{max-size}} bytes. Please make your code smaller and try again or request assistance on https://forum.freecodecamp.org",
"challenge-submit-too-big": "Sorry, you cannot submit your code. Your code is {{user-size}} bytes. We allow a maximum of {{max-size}} bytes. Please make your code smaller and try again or request assistance on https://forum.freecodecamp.org"
"challenge-submit-too-big": "Sorry, you cannot submit your code. Your code is {{user-size}} bytes. We allow a maximum of {{max-size}} bytes. Please make your code smaller and try again or request assistance on https://forum.freecodecamp.org",
"invalid-update-flag": "You are attempting to access forbidden resources. Please request assistance on https://forum.freecodecamp.org if this is a valid request."
},
"validation": {
"max-characters": "There is a maximum limit of 288 characters, you have {{charsLeft}} left",

View File

@ -114,8 +114,12 @@ let blocklist = [
'twitch',
'unsubscribe',
'unsubscribed',
'update-my-honesty',
'update-my-portfolio',
'update-my-profile-ui',
'update-my-quincy-email',
'update-my-socials',
'update-my-sound',
'update-my-theme',
'update-my-username',
'user',