Fix insane bug where some profiles cause client hang

Some code was converting the customs object to an array with millions of elements, which caused iteration and ipc transport (json serialize) to hang the client for many minutes at a time.

This is probably because incorrect lodash functions were used on the customs. Lodash would convert it to array and then it would get stored and turn up as array every time, corrupting storage and ensuring client denial of service.

This fixes that bug by avoiding using incorrect lodash functions that convert things to arrays with millions of elements.

It also retroactively fixes profiles that were stored with array customs, so the client won't crash.
This commit is contained in:
buttercheezii 2021-12-06 05:00:44 -08:00
parent b8e6d7fb09
commit c539dba6a8
3 changed files with 27 additions and 16 deletions

View File

@ -213,7 +213,7 @@ export default class CharacterPreview extends Vue {
updateCustoms(): void {
this.customs = _.orderBy(
_.map(
_.reject(this.character!.character.customs || [], (c) => _.isUndefined(c)) as CustomKink[],
_.reject(Object.values(this.character!.character.customs ?? []), (c) => _.isUndefined(c)) as CustomKink[],
(c: CustomKink) => _.assign(
{},
c,

View File

@ -337,9 +337,9 @@ export class Matcher {
return _.map(
speciesOptions,
(species) => {
const nc = _.cloneDeep(c);
nc.infotags[TagId.Species] = { string: species };
// Avoid _.cloneDeep because it chokes on array-like objects with very large keys
// _.cloneDeep will happily make arrays with 41 million elements
const nc = {...c, infotags: {...c.infotags, [TagId.Species]: {string: species}}};
return { character: nc, analysis: new CharacterAnalysis(nc) };
}
@ -975,18 +975,14 @@ export class Matcher {
private getAllStandardKinks(c: Character): { [key: number]: KinkChoice } {
const kinks = _.pickBy(c.kinks, _.isString);
_.each(
c.customs,
(custom: any) => {
if (!custom) {
return;
// Avoid using _.forEach on c.customs because lodash thinks it is an array
for (const custom of Object.values(c.customs)) {
if (custom) {
const children = (custom as any).children ?? {};
_.each(children, (child) => kinks[child] = custom.choice);
}
const children = (custom.children) ? custom.children : {};
_.each(children, (child) => kinks[child] = custom.choice);
}
);
}
return kinks as any;
}

View File

@ -26,7 +26,22 @@ export class WorkerStore implements PermanentIndexedStore {
async getProfile(name: string): Promise<ProfileRecord | undefined> {
return this.workerClient.request('get', { name });
const record: ProfileRecord | undefined = await this.workerClient.request('get', { name });
// fix custom kinks to prevent hangs
if (record && Array.isArray(record.profileData.character.customs)) {
// fix customs because it will crash the client
const customsObject: ProfileRecord['profileData']['character']['customs'] = {};
for (const [key, value] of Object.entries(record.profileData.character.customs)) {
if (value !== undefined) customsObject[key] = value;
}
record.profileData.character.customs = customsObject;
}
return record;
}