1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-11-24 08:12:24 +02:00

Desktop: Fixes #7621: Certain plugins could create invalid settings, which could result in a crash

This commit is contained in:
Laurent Cozic 2023-01-17 15:34:04 +00:00
parent f7682d3da3
commit dc5dc94ed5
3 changed files with 60 additions and 25 deletions

View File

@ -453,6 +453,12 @@ class ConfigScreenComponent extends React.Component<any, any> {
inputStyle.marginBottom = subLabel.marginBottom;
const splitCmd = (cmdString: string) => {
// Normally not necessary but certain plugins found a way to
// set the set the value to "undefined", leading to a crash.
// This is now fixed at the model level but to be sure we
// check here too, to handle any already existing data.
// https://github.com/laurent22/joplin/issues/7621
if (!cmdString) cmdString = '';
const path = pathUtils.extractExecutablePath(cmdString);
const args = cmdString.substr(path.length + 1);
return [pathUtils.unquotePath(path), args];

View File

@ -379,4 +379,19 @@ describe('models/Setting', function() {
}
});
test('values should not be undefined when they are set', async () => {
Setting.setValue('locale', undefined);
expect(Setting.value('locale')).toBe('');
});
test('values should not be undefined when registering a setting', async () => {
await Setting.registerSetting('myCustom', {
public: true,
value: undefined,
type: Setting.TYPE_STRING,
});
expect(Setting.value('myCustom')).toBe('');
});
});

View File

@ -1740,31 +1740,45 @@ class Setting extends BaseModel {
if (!key.match(/^[a-zA-Z0-9_\-.]+$/)) throw new Error(`Key must only contain characters /a-zA-Z0-9_-./ : ${key}`);
}
private static validateType(type: SettingItemType) {
if (!Number.isInteger(type)) throw new Error(`Setting type is not an integer: ${type}`);
if (type < 0) throw new Error(`Invalid setting type: ${type}`);
}
static async registerSetting(key: string, metadataItem: SettingItem) {
if (metadataItem.isEnum && !metadataItem.options) throw new Error('The `options` property is required for enum types');
try {
if (metadataItem.isEnum && !metadataItem.options) throw new Error('The `options` property is required for enum types');
this.validateKey(key);
this.validateKey(key);
this.validateType(metadataItem.type);
this.customMetadata_[key] = metadataItem;
this.customMetadata_[key] = {
...metadataItem,
value: this.formatValue(metadataItem.type, metadataItem.value),
};
// Clear cache
this.metadata_ = null;
this.keys_ = null;
// Clear cache
this.metadata_ = null;
this.keys_ = null;
// Reload the value from the database, if it was already present
const valueRow = await this.loadOne(key);
if (valueRow) {
this.cache_.push({
// Reload the value from the database, if it was already present
const valueRow = await this.loadOne(key);
if (valueRow) {
this.cache_.push({
key: key,
value: this.formatValue(key, valueRow.value),
});
}
this.dispatch({
type: 'SETTING_UPDATE_ONE',
key: key,
value: this.formatValue(key, valueRow.value),
value: this.value(key),
});
} catch (error) {
error.message = `Could not register setting "${key}": ${error.message}`;
throw error;
}
this.dispatch({
type: 'SETTING_UPDATE_ONE',
key: key,
value: this.value(key),
});
}
static async registerSection(name: string, source: SettingSectionSource, section: SettingSection) {
@ -2119,12 +2133,12 @@ class Setting extends BaseModel {
return md.filter ? md.filter(value) : value;
}
static formatValue(key: string, value: any) {
const md = this.settingMetadata(key);
static formatValue(key: string | SettingItemType, value: any) {
const type = typeof key === 'string' ? this.settingMetadata(key).type : key;
if (md.type === SettingItemType.Int) return !value ? 0 : Math.floor(Number(value));
if (type === SettingItemType.Int) return !value ? 0 : Math.floor(Number(value));
if (md.type === SettingItemType.Bool) {
if (type === SettingItemType.Bool) {
if (typeof value === 'string') {
value = value.toLowerCase();
if (value === 'true') return true;
@ -2134,26 +2148,26 @@ class Setting extends BaseModel {
return !!value;
}
if (md.type === SettingItemType.Array) {
if (type === SettingItemType.Array) {
if (!value) return [];
if (Array.isArray(value)) return value;
if (typeof value === 'string') return JSON.parse(value);
return [];
}
if (md.type === SettingItemType.Object) {
if (type === SettingItemType.Object) {
if (!value) return {};
if (typeof value === 'object') return value;
if (typeof value === 'string') return JSON.parse(value);
return {};
}
if (md.type === SettingItemType.String) {
if (type === SettingItemType.String) {
if (!value) return '';
return `${value}`;
}
throw new Error(`Unhandled value type: ${md.type}`);
throw new Error(`Unhandled value type: ${type}`);
}
static value(key: string) {