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:
committed by
GitHub
parent
bb0f9c8036
commit
9ea7018740
@ -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'
|
||||
});
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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',
|
||||
|
Reference in New Issue
Block a user