mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-20 22:42:53 -05:00
Added labs setting input validation
refs https://github.com/TryGhost/Team/issues/757 - To safeguard from mise of a very permissing "object" value of the "labs" setting this change introduces an "allowlist" approach to filtering unrecognized labs flags - Should allow maintainers to have a clear view of which labs flags are currently in use and manage them accordingly
This commit is contained in:
parent
8ab43b84d5
commit
cd35358fdb
8 changed files with 75 additions and 9 deletions
|
@ -2,6 +2,7 @@ const _ = require('lodash');
|
||||||
const url = require('./utils/url');
|
const url = require('./utils/url');
|
||||||
const typeGroupMapper = require('../../../../shared/serializers/input/utils/settings-filter-type-group-mapper');
|
const typeGroupMapper = require('../../../../shared/serializers/input/utils/settings-filter-type-group-mapper');
|
||||||
const settingsCache = require('../../../../../services/settings/cache');
|
const settingsCache = require('../../../../../services/settings/cache');
|
||||||
|
const {WRITABLE_KEYS_ALLOWLIST} = require('../../../../../services/labs');
|
||||||
|
|
||||||
const DEPRECATED_SETTINGS = [
|
const DEPRECATED_SETTINGS = [
|
||||||
'bulk_email_settings',
|
'bulk_email_settings',
|
||||||
|
@ -158,6 +159,19 @@ module.exports = {
|
||||||
setting.key = 'lang';
|
setting.key = 'lang';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (setting.key === 'labs') {
|
||||||
|
const inputLabsValue = JSON.parse(setting.value);
|
||||||
|
const filteredLabsValue = {};
|
||||||
|
|
||||||
|
for (const flag in inputLabsValue) {
|
||||||
|
if (WRITABLE_KEYS_ALLOWLIST.includes(flag)) {
|
||||||
|
filteredLabsValue[flag] = inputLabsValue[flag];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setting.value = JSON.stringify(filteredLabsValue);
|
||||||
|
}
|
||||||
|
|
||||||
setting = url.forSetting(setting);
|
setting = url.forSetting(setting);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ const _ = require('lodash');
|
||||||
const url = require('./utils/url');
|
const url = require('./utils/url');
|
||||||
const typeGroupMapper = require('../../../../shared/serializers/input/utils/settings-filter-type-group-mapper');
|
const typeGroupMapper = require('../../../../shared/serializers/input/utils/settings-filter-type-group-mapper');
|
||||||
const settingsCache = require('../../../../../services/settings/cache');
|
const settingsCache = require('../../../../../services/settings/cache');
|
||||||
|
const {WRITABLE_KEYS_ALLOWLIST} = require('../../../../../services/labs');
|
||||||
|
|
||||||
const DEPRECATED_SETTINGS = [
|
const DEPRECATED_SETTINGS = [
|
||||||
'bulk_email_settings',
|
'bulk_email_settings',
|
||||||
|
@ -138,6 +139,19 @@ module.exports = {
|
||||||
setting.value = JSON.parse(setting.value).isActive;
|
setting.value = JSON.parse(setting.value).isActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (setting.key === 'labs') {
|
||||||
|
const inputLabsValue = JSON.parse(setting.value);
|
||||||
|
const filteredLabsValue = {};
|
||||||
|
|
||||||
|
for (const value in inputLabsValue) {
|
||||||
|
if (WRITABLE_KEYS_ALLOWLIST.includes(value)) {
|
||||||
|
filteredLabsValue[value] = inputLabsValue[value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setting.value = JSON.stringify(filteredLabsValue);
|
||||||
|
}
|
||||||
|
|
||||||
setting = url.forSetting(setting);
|
setting = url.forSetting(setting);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ const _ = require('lodash');
|
||||||
const url = require('./utils/url');
|
const url = require('./utils/url');
|
||||||
const typeGroupMapper = require('../../../../shared/serializers/input/utils/settings-filter-type-group-mapper');
|
const typeGroupMapper = require('../../../../shared/serializers/input/utils/settings-filter-type-group-mapper');
|
||||||
const settingsCache = require('../../../../../services/settings/cache');
|
const settingsCache = require('../../../../../services/settings/cache');
|
||||||
|
const {WRITABLE_KEYS_ALLOWLIST} = require('../../../../../services/labs');
|
||||||
|
|
||||||
const DEPRECATED_SETTINGS = [
|
const DEPRECATED_SETTINGS = [
|
||||||
'bulk_email_settings',
|
'bulk_email_settings',
|
||||||
|
@ -154,6 +155,19 @@ module.exports = {
|
||||||
setting.value = JSON.parse(setting.value).isActive;
|
setting.value = JSON.parse(setting.value).isActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (setting.key === 'labs') {
|
||||||
|
const inputLabsValue = JSON.parse(setting.value);
|
||||||
|
const filteredLabsValue = {};
|
||||||
|
|
||||||
|
for (const value in inputLabsValue) {
|
||||||
|
if (WRITABLE_KEYS_ALLOWLIST.includes(value)) {
|
||||||
|
filteredLabsValue[value] = inputLabsValue[value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setting.value = JSON.stringify(filteredLabsValue);
|
||||||
|
}
|
||||||
|
|
||||||
setting = url.forSetting(setting);
|
setting = url.forSetting(setting);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@ const i18n = require('../../shared/i18n');
|
||||||
const errors = require('@tryghost/errors');
|
const errors = require('@tryghost/errors');
|
||||||
const validation = require('../data/validation');
|
const validation = require('../data/validation');
|
||||||
const urlUtils = require('../../shared/url-utils');
|
const urlUtils = require('../../shared/url-utils');
|
||||||
|
const {WRITABLE_KEYS_ALLOWLIST} = require('../services/labs');
|
||||||
|
|
||||||
const internalContext = {context: {internal: true}};
|
const internalContext = {context: {internal: true}};
|
||||||
let Settings;
|
let Settings;
|
||||||
let defaultSettings;
|
let defaultSettings;
|
||||||
|
@ -341,6 +343,17 @@ Settings = ghostBookshelf.Model.extend({
|
||||||
throw new errors.ValidationError(validationErrors.join('\n'));
|
throw new errors.ValidationError(validationErrors.join('\n'));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async labs(model) {
|
||||||
|
const flags = JSON.parse(model.get('value'));
|
||||||
|
|
||||||
|
for (const flag in flags) {
|
||||||
|
if (!WRITABLE_KEYS_ALLOWLIST.includes(flag)) {
|
||||||
|
throw new errors.ValidationError({
|
||||||
|
message: `Settings lab value cannot have value other then ${WRITABLE_KEYS_ALLOWLIST.join(', ')}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
async stripe_plans(model, options) {
|
async stripe_plans(model, options) {
|
||||||
const plans = JSON.parse(model.get('value'));
|
const plans = JSON.parse(model.get('value'));
|
||||||
for (const plan of plans) {
|
for (const plan of plans) {
|
||||||
|
|
|
@ -6,6 +6,14 @@ const i18n = require('../../shared/i18n');
|
||||||
const logging = require('../../shared/logging');
|
const logging = require('../../shared/logging');
|
||||||
const settingsCache = require('../services/settings/cache');
|
const settingsCache = require('../services/settings/cache');
|
||||||
|
|
||||||
|
// NOTE: this allowlist is meant to be used to filter out any unexpected
|
||||||
|
// input for the "labs" setting value
|
||||||
|
const WRITABLE_KEYS_ALLOWLIST = [
|
||||||
|
'activitypub'
|
||||||
|
];
|
||||||
|
|
||||||
|
module.exports.WRITABLE_KEYS_ALLOWLIST = WRITABLE_KEYS_ALLOWLIST;
|
||||||
|
|
||||||
module.exports.getAll = () => ({
|
module.exports.getAll = () => ({
|
||||||
members: settingsCache.get('members_signup_access') !== 'none'
|
members: settingsCache.get('members_signup_access') !== 'none'
|
||||||
});
|
});
|
||||||
|
|
|
@ -653,12 +653,13 @@ describe('Settings API (canary)', function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Can edit labs', async function () {
|
it('Can edit only allowed labs keys', async function () {
|
||||||
const settingToChange = {
|
const settingToChange = {
|
||||||
settings: [{
|
settings: [{
|
||||||
key: 'labs',
|
key: 'labs',
|
||||||
value: JSON.stringify({
|
value: JSON.stringify({
|
||||||
matchHelper: true
|
activitypub: true,
|
||||||
|
gibberish: true
|
||||||
})
|
})
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
@ -680,7 +681,7 @@ describe('Settings API (canary)', function () {
|
||||||
jsonResponse.settings[0].key.should.eql('labs');
|
jsonResponse.settings[0].key.should.eql('labs');
|
||||||
|
|
||||||
jsonResponse.settings[0].value.should.eql(JSON.stringify({
|
jsonResponse.settings[0].value.should.eql(JSON.stringify({
|
||||||
matchHelper: true
|
activitypub: true
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -521,12 +521,13 @@ describe('Settings API (v2)', function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Can edit labs', async function () {
|
it('Can edit only allowed labs keys', async function () {
|
||||||
const settingToChange = {
|
const settingToChange = {
|
||||||
settings: [{
|
settings: [{
|
||||||
key: 'labs',
|
key: 'labs',
|
||||||
value: JSON.stringify({
|
value: JSON.stringify({
|
||||||
matchHelper: true
|
activitypub: true,
|
||||||
|
gibberish: true
|
||||||
})
|
})
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
@ -548,7 +549,7 @@ describe('Settings API (v2)', function () {
|
||||||
jsonResponse.settings[0].key.should.eql('labs');
|
jsonResponse.settings[0].key.should.eql('labs');
|
||||||
|
|
||||||
jsonResponse.settings[0].value.should.eql(JSON.stringify({
|
jsonResponse.settings[0].value.should.eql(JSON.stringify({
|
||||||
matchHelper: true
|
activitypub: true
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -464,12 +464,13 @@ describe('Settings API (v3)', function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Can edit labs', async function () {
|
it('Can edit only allowed labs keys', async function () {
|
||||||
const settingToChange = {
|
const settingToChange = {
|
||||||
settings: [{
|
settings: [{
|
||||||
key: 'labs',
|
key: 'labs',
|
||||||
value: JSON.stringify({
|
value: JSON.stringify({
|
||||||
matchHelper: true
|
activitypub: true,
|
||||||
|
gibberish: true
|
||||||
})
|
})
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
@ -491,7 +492,7 @@ describe('Settings API (v3)', function () {
|
||||||
jsonResponse.settings[0].key.should.eql('labs');
|
jsonResponse.settings[0].key.should.eql('labs');
|
||||||
|
|
||||||
jsonResponse.settings[0].value.should.eql(JSON.stringify({
|
jsonResponse.settings[0].value.should.eql(JSON.stringify({
|
||||||
matchHelper: true
|
activitypub: true
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue