fix(security): close the remaining image-security default gaps
Round-two review follow-ups. Settings survive the tab round trip. GET returns setting_value without decoding it and ImageSecurityTab PUTs the whole fetched object back through JSON.stringify, so on SQLite one visit to the tab re-encodes every value it read. A single parse then yields the string "true", the type checks reject it, and the defaults go quietly dead — the exact bug this change exists to fix, returning by a different route. The reader now unwraps until the value stops being a JSON string, bounded. Array overrides rejected. express-validator applies isInt/isIn/isBoolean element-wise, so `image_quality: [72]` passed the chain and arrived still an array — a PG insert error, and `[false]` coerced to true by formatBoolean. Both create routes now use .not().isArray(), and the shared resolver ignores non-scalars for any future caller. Two more creation paths covered. quoteService.convertToEvent builds its own events row, so CRM-converted galleries fell back to column defaults. /:id/duplicate copies fifteen source columns including enable_devtools_protection but missed these four, so duplicating a 'maximum' gallery produced a 'standard' one — a duplicate now inherits the source's values, not the current globals, since copying the gallery is the point. The PUT /:id chain has the same array weakness. Pre-existing and outside this fix; left alone deliberately. Refs #1296
This commit is contained in:
@@ -107,6 +107,39 @@ describe('image-security creation defaults', () => {
|
||||
await expect(getImageSecurityDefaults()).resolves.toEqual({});
|
||||
});
|
||||
|
||||
describe('double-encoded settings (the settings tab round trip)', () => {
|
||||
// GET returns setting_value undecoded and the tab PUTs the whole object
|
||||
// back through JSON.stringify, so on SQLite one visit to the tab turns
|
||||
// every value it read into a doubly-encoded string. A single parse left
|
||||
// a string behind, the type checks rejected it, and the defaults went
|
||||
// silently dead again.
|
||||
const setRaw = async (key, raw) => {
|
||||
await db('app_settings')
|
||||
.insert({ setting_key: key, setting_value: raw, setting_type: 'security' })
|
||||
.onConflict('setting_key').merge();
|
||||
};
|
||||
|
||||
it('reads a double-encoded boolean', async () => {
|
||||
await setRaw('enable_canvas_rendering', JSON.stringify(JSON.stringify(true)));
|
||||
expect(await getImageSecurityDefaults()).toEqual({ use_canvas_rendering: true });
|
||||
});
|
||||
|
||||
it('reads a double-encoded protection level', async () => {
|
||||
await setRaw('default_protection_level', JSON.stringify(JSON.stringify('enhanced')));
|
||||
expect(await getImageSecurityDefaults()).toEqual({ protection_level: 'enhanced' });
|
||||
});
|
||||
|
||||
it('reads a double-encoded integer', async () => {
|
||||
await setRaw('default_image_quality', JSON.stringify(JSON.stringify(72)));
|
||||
expect(await getImageSecurityDefaults()).toEqual({ image_quality: 72 });
|
||||
});
|
||||
|
||||
it('still rejects a malformed value however many times it was encoded', async () => {
|
||||
await setRaw('default_image_quality', JSON.stringify(JSON.stringify('72oops')));
|
||||
expect(await getImageSecurityDefaults()).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveImageSecurityColumns', () => {
|
||||
it('omits every column when neither the request nor the settings supply one', () => {
|
||||
expect(resolveImageSecurityColumns({}, {})).toEqual({});
|
||||
|
||||
Reference in New Issue
Block a user