mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Fix issue with lodash's map interpreting objects with length as array-like
Summary: Here's a series of badness that easily leads to a crash, in reverse order: - Lodash's map() function interprets an object with a .length property as an array. - Some very old code generated human-friendly descriptions of user actions, applying map() to parts of them. It so happens that this generated description isn't even used. - If a user action is encountered with a sufficiently large length propery, map() would exhaust the server memory. Fixed by removing old unneeded code, and replacing some other occurrences of lodash's map() with native equivalents. Test Plan: Tested manually on a local reproduction of the issue. Reviewers: paulfitz Reviewed By: paulfitz Subscribers: paulfitz Differential Revision: https://phab.getgrist.com/D3938
This commit is contained in:
@@ -6,7 +6,6 @@ import * as dispose from 'app/client/lib/dispose';
|
||||
import dom from 'app/client/lib/dom';
|
||||
import {timeFormat} from 'app/common/timeFormat';
|
||||
import * as ko from 'knockout';
|
||||
import map = require('lodash/map');
|
||||
|
||||
import koArray from 'app/client/lib/koArray';
|
||||
import {KoArray} from 'app/client/lib/koArray';
|
||||
@@ -17,8 +16,8 @@ import {GristDoc} from 'app/client/components/GristDoc';
|
||||
import {ActionGroup} from 'app/common/ActionGroup';
|
||||
import {ActionSummary, asTabularDiffs, defunctTableName, getAffectedTables,
|
||||
LabelDelta} from 'app/common/ActionSummary';
|
||||
import {CellDelta} from 'app/common/TabularDiff';
|
||||
import {IDomComponent} from 'grainjs';
|
||||
import {CellDelta, TabularDiff} from 'app/common/TabularDiff';
|
||||
import {DomContents, IDomComponent} from 'grainjs';
|
||||
import {makeT} from 'app/client/lib/localization';
|
||||
|
||||
/**
|
||||
@@ -141,12 +140,12 @@ export class ActionLog extends dispose.Disposable implements IDomComponent {
|
||||
* @param {string} txt - a textual description of the action
|
||||
* @param {ActionGroupWithState} ag - the full action information we have
|
||||
*/
|
||||
public renderTabularDiffs(sum: ActionSummary, txt: string, ag?: ActionGroupWithState) {
|
||||
public renderTabularDiffs(sum: ActionSummary, txt: string, ag?: ActionGroupWithState): HTMLElement {
|
||||
const act = asTabularDiffs(sum);
|
||||
const editDom = dom('div',
|
||||
this._renderTableSchemaChanges(sum, ag),
|
||||
this._renderColumnSchemaChanges(sum, ag),
|
||||
map(act, (tdiff, table) => {
|
||||
Object.entries(act).map(([table, tdiff]: [string, TabularDiff]) => {
|
||||
if (tdiff.cells.length === 0) { return dom('div'); }
|
||||
return dom('table.action_log_table',
|
||||
koDom.show(() => this._showForTable(table, ag)),
|
||||
@@ -238,7 +237,7 @@ export class ActionLog extends dispose.Disposable implements IDomComponent {
|
||||
'Loading...'),
|
||||
koDom.foreach(this._displayStack, (ag: ActionGroupWithState) => {
|
||||
const timestamp = ag.time ? timeFormat("D T", new Date(ag.time)) : "";
|
||||
let desc = ag.desc || "";
|
||||
let desc: DomContents = ag.desc || "";
|
||||
if (ag.actionSummary) {
|
||||
desc = this.renderTabularDiffs(ag.actionSummary, desc, ag);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
const _ = require('underscore');
|
||||
const ko = require('knockout');
|
||||
const moment = require('moment-timezone');
|
||||
const {getSelectionDesc} = require('app/common/DocActions');
|
||||
const {nativeCompare, roundDownToMultiple, waitObs} = require('app/common/gutil');
|
||||
const gutil = require('app/common/gutil');
|
||||
const MANUALSORT = require('app/common/gristTypes').MANUALSORT;
|
||||
@@ -646,20 +645,7 @@ BaseView.prototype.sendPasteActions = function(cutCallback, actions) {
|
||||
// If the cut occurs on an edit restricted cell, there may be no cut action.
|
||||
if (cutAction) { actions.unshift(cutAction); }
|
||||
}
|
||||
return this.gristDoc.docData.sendActions(actions,
|
||||
this._getPasteDesc(actions[actions.length - 1], cutAction));
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a string which describes a cut/copy action.
|
||||
*/
|
||||
BaseView.prototype._getPasteDesc = function(pasteAction, optCutAction) {
|
||||
if (optCutAction) {
|
||||
return `Moved ${getSelectionDesc(optCutAction, true)} to ` +
|
||||
`${getSelectionDesc(pasteAction, true)}.`;
|
||||
} else {
|
||||
return `Pasted data to ${getSelectionDesc(pasteAction, true)}.`;
|
||||
}
|
||||
return this.gristDoc.docData.sendActions(actions);
|
||||
};
|
||||
|
||||
BaseView.prototype.buildDom = function() {
|
||||
|
||||
@@ -8,7 +8,6 @@ import {safeJsonParse} from 'app/common/gutil';
|
||||
import type {TableData} from 'app/common/TableData';
|
||||
import {tsvEncode} from 'app/common/tsvFormat';
|
||||
import {dom} from 'grainjs';
|
||||
import map = require('lodash/map');
|
||||
import zipObject = require('lodash/zipObject');
|
||||
|
||||
const G = getBrowserGlobals('document', 'DOMParser');
|
||||
@@ -134,8 +133,11 @@ export function parsePasteHtml(data: string): RichPasteObject[][] {
|
||||
}
|
||||
|
||||
// Helper function to add css style properties to an html tag
|
||||
function _styleAttr(style: object) {
|
||||
return map(style, (value, prop) => `${prop}: ${value};`).join(' ');
|
||||
function _styleAttr(style: object|undefined) {
|
||||
if (typeof style !== 'object') {
|
||||
return '';
|
||||
}
|
||||
return Object.entries(style).map(([prop, value]) => `${prop}: ${value};`).join(' ');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user