Fix(routing): add protections against malformed data
This commit is contained in:
@ -1,5 +1,6 @@
|
|||||||
import { compose } from 'redux';
|
import { compose } from 'redux';
|
||||||
import { bonfire, html, js } from '../../utils/challengeTypes';
|
import { bonfire, html, js } from '../../utils/challengeTypes';
|
||||||
|
import protect from '../../utils/empty-protector';
|
||||||
|
|
||||||
export function encodeScriptTags(value) {
|
export function encodeScriptTags(value) {
|
||||||
return value
|
return value
|
||||||
@ -78,19 +79,6 @@ export function loggerToStr(args) {
|
|||||||
.reduce((str, arg) => str + arg + '\n', '');
|
.reduce((str, arg) => str + arg + '\n', '');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFirstChallenge(
|
|
||||||
{ superBlock, block, challenge },
|
|
||||||
result
|
|
||||||
) {
|
|
||||||
return challenge[
|
|
||||||
block[
|
|
||||||
superBlock[
|
|
||||||
result[0]
|
|
||||||
].blocks[0]
|
|
||||||
].challenges[0]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getNextChallenge(
|
export function getNextChallenge(
|
||||||
current,
|
current,
|
||||||
entities,
|
entities,
|
||||||
@ -285,20 +273,6 @@ export function getMouse(e, [dx, dy]) {
|
|||||||
return [pageX - dx, pageY - dy];
|
return [pageX - dx, pageY - dy];
|
||||||
}
|
}
|
||||||
|
|
||||||
const emptyProtector = {
|
|
||||||
blocks: [],
|
|
||||||
challenges: []
|
|
||||||
};
|
|
||||||
// protect against malformed data
|
|
||||||
function protect(block) {
|
|
||||||
// if no block or block has no challenges or blocks
|
|
||||||
// use protector
|
|
||||||
if (!block || !(block.challenges || block.blocks)) {
|
|
||||||
return emptyProtector;
|
|
||||||
}
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
|
|
||||||
// interface Node {
|
// interface Node {
|
||||||
// isHidden: Boolean,
|
// isHidden: Boolean,
|
||||||
// children: Void|[ ...Node ],
|
// children: Void|[ ...Node ],
|
||||||
|
13
common/app/utils/empty-protector.js
Normal file
13
common/app/utils/empty-protector.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
const emptyProtector = {
|
||||||
|
blocks: [],
|
||||||
|
challenges: []
|
||||||
|
};
|
||||||
|
// protect against malformed map data
|
||||||
|
// protect(block: { challenges: [], block: [] }|Void) => block|emptyProtector
|
||||||
|
export default function protect(block) {
|
||||||
|
// if no block or block has no challenges or blocks
|
||||||
|
if (!block || !(block.challenges || block.blocks)) {
|
||||||
|
return emptyProtector;
|
||||||
|
}
|
||||||
|
return block;
|
||||||
|
}
|
44
common/utils/get-first-challenge.js
Normal file
44
common/utils/get-first-challenge.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import emptyProtector from '../app/utils/empty-protector';
|
||||||
|
|
||||||
|
export function checkMapData(
|
||||||
|
{
|
||||||
|
entities: {
|
||||||
|
challenge,
|
||||||
|
block,
|
||||||
|
superBlock,
|
||||||
|
challengeIdToName
|
||||||
|
},
|
||||||
|
result
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
!challenge ||
|
||||||
|
!block ||
|
||||||
|
!superBlock ||
|
||||||
|
!challengeIdToName ||
|
||||||
|
!result ||
|
||||||
|
!result.length
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
'entities not found, db may not be properly seeded. Crashing hard'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// getFirstChallenge(
|
||||||
|
// map: {
|
||||||
|
// entities: { challenge: Object, block: Object, superBlock: Object },
|
||||||
|
// result: [...superBlockDashedName: String]
|
||||||
|
// }
|
||||||
|
// ) => Challenge|Void
|
||||||
|
export function getFirstChallenge({
|
||||||
|
entities: { superBlock, block, challenge },
|
||||||
|
result
|
||||||
|
}) {
|
||||||
|
return challenge[
|
||||||
|
emptyProtector(block[
|
||||||
|
emptyProtector(superBlock[
|
||||||
|
result[0]
|
||||||
|
]).blocks[0]
|
||||||
|
]).challenges[0]
|
||||||
|
];
|
||||||
|
}
|
@ -1,11 +1,15 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
// import { Observable, Scheduler } from 'rx';
|
|
||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
import accepts from 'accepts';
|
import accepts from 'accepts';
|
||||||
|
import dedent from 'dedent';
|
||||||
|
|
||||||
import { ifNoUserSend } from '../utils/middleware';
|
import { ifNoUserSend } from '../utils/middleware';
|
||||||
import { cachedMap } from '../utils/map';
|
import { cachedMap } from '../utils/map';
|
||||||
import createNameIdMap from '../../common/utils/create-name-id-map';
|
import createNameIdMap from '../../common/utils/create-name-id-map';
|
||||||
|
import {
|
||||||
|
checkMapData,
|
||||||
|
getFirstChallenge
|
||||||
|
} from '../../common/utils/get-first-challenge';
|
||||||
|
|
||||||
const log = debug('fcc:boot:challenges');
|
const log = debug('fcc:boot:challenges');
|
||||||
|
|
||||||
@ -274,40 +278,34 @@ export default function(app) {
|
|||||||
result,
|
result,
|
||||||
entities: createNameIdMap(entities)
|
entities: createNameIdMap(entities)
|
||||||
}))
|
}))
|
||||||
.map(({
|
.map(map => {
|
||||||
result,
|
checkMapData(map);
|
||||||
entities: {
|
const {
|
||||||
challenge: challengeMap,
|
entities: { challenge: challengeMap, challengeIdToName }
|
||||||
block: blockMap,
|
} = map;
|
||||||
superBlock: superBlockMap,
|
|
||||||
challengeIdToName
|
|
||||||
}
|
|
||||||
}) => {
|
|
||||||
let finalChallenge;
|
let finalChallenge;
|
||||||
const dashedName = challengeIdToName[user && user.currentChallengeId];
|
const dashedName = challengeIdToName[user && user.currentChallengeId];
|
||||||
finalChallenge = challengeMap[dashedName];
|
finalChallenge = challengeMap[dashedName];
|
||||||
if (
|
|
||||||
!challengeMap ||
|
|
||||||
!blockMap ||
|
|
||||||
!superBlockMap ||
|
|
||||||
!result ||
|
|
||||||
!result.length
|
|
||||||
) {
|
|
||||||
throw new Error(
|
|
||||||
'entities not found, db may not be properly seeded. Crashing hard'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// redirect to first challenge
|
// redirect to first challenge
|
||||||
if (!finalChallenge) {
|
if (!finalChallenge) {
|
||||||
finalChallenge = challengeMap[
|
finalChallenge = getFirstChallenge(map);
|
||||||
block[
|
|
||||||
superBlockMap[
|
|
||||||
result[0]
|
|
||||||
].blocks[0]
|
|
||||||
].challenges[0]
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
const { block, dashedName: finalDashedName } = finalChallenge || {};
|
const { block, dashedName: finalDashedName } = finalChallenge || {};
|
||||||
|
if (!finalDashedName || !block) {
|
||||||
|
// this should normally not be hit if database is properly seeded
|
||||||
|
console.error(new Error(dedent`
|
||||||
|
Attemped to find '${dashedName}'
|
||||||
|
from '${user && user.currentChallengeId || 'no challenge id found'}'
|
||||||
|
but came up empty.
|
||||||
|
db may not be properly seeded.
|
||||||
|
`));
|
||||||
|
if (dashedName) {
|
||||||
|
// attempt to find according to dashedName
|
||||||
|
return `/challenges/${dashedName}`;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
return `/challenges/${block}/${finalDashedName}`;
|
return `/challenges/${block}/${finalDashedName}`;
|
||||||
})
|
})
|
||||||
.subscribe(
|
.subscribe(
|
||||||
|
Reference in New Issue
Block a user