Skip to content

Commit

Permalink
redux: Scaffolding for custom replace/revive logic.
Browse files Browse the repository at this point in the history
Be sure to use our SerializeEscaped wrapper, introduced earlier in
this series, for proper escaping of the '__serializedType__' key, to
prevent a security hole.

This code looks almost exactly the same as it would without our
wrapper.

One key difference is that we use the constant
SERIALIZED_TYPE_FIELD_NAME, exposed by our wrapper and used in its
implementation, to be certain that we're using the same field name
that gets escaped. Fortunately, this is very easy to do.

(If we had used a different string, then, as long as that string
were consistent between our custom replacer and reviver functions,
(1) things would basically work, with no obvious indications that
anything was wrong, and (2) the security hole would reopen.)
  • Loading branch information
Chris Bobbe committed Apr 29, 2020
1 parent 10d4fdf commit 443ba2d
Showing 1 changed file with 28 additions and 0 deletions.
28 changes: 28 additions & 0 deletions src/boot/store.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* @flow strict-local */
import { applyMiddleware, compose, createStore } from 'redux';
import type { Store } from 'redux';
import Immutable from 'immutable';
import { persistStore, autoRehydrate } from '../third/redux-persist';
import type { Config } from '../third/redux-persist';

Expand All @@ -9,6 +10,9 @@ import rootReducer from './reducers';
import middleware from './middleware';
import ZulipAsyncStorage from './ZulipAsyncStorage';
import createMigration from '../redux-persist-migrate/index';
import * as SerializeEscaped from './SerializeEscaped';

const { SERIALIZED_TYPE_FIELD_NAME } = SerializeEscaped;

// AsyncStorage.clear(); // use to reset storage during development

Expand Down Expand Up @@ -143,10 +147,34 @@ const migrations: { [string]: (GlobalState) => GlobalState } = {
}),
};

const customReplacer = (key, value, defaultReplacer) =>
// Scaffolding for the next commit, where we replace/revive ZulipVersion
defaultReplacer(key, value);
const customReviver = (key, value, defaultReviver) => {
if (value !== null && typeof value === 'object' && SERIALIZED_TYPE_FIELD_NAME in value) {
// Scaffolding for the next commit, where we replace/revive ZulipVersion
const data = value.data; // eslint-disable-line no-unused-vars
switch (value[SERIALIZED_TYPE_FIELD_NAME]) {
default:
// Fall back to defaultReviver, below
}
}
return defaultReviver(key, value);
};

const { stringify, parse } = SerializeEscaped.immutable(
Immutable,
null,
customReplacer,
customReviver,
);

const reduxPersistConfig: Config = {
whitelist: [...storeKeys, ...cacheKeys],
// $FlowFixMe: https://github.com/rt2zz/redux-persist/issues/823
storage: ZulipAsyncStorage,
serialize: stringify,
deserialize: parse,
};

const store: Store<GlobalState, Action> = createStore(
Expand Down

0 comments on commit 443ba2d

Please sign in to comment.