Fix(routing): add protections against malformed data
This commit is contained in:
@ -1,5 +1,6 @@
|
||||
import { compose } from 'redux';
|
||||
import { bonfire, html, js } from '../../utils/challengeTypes';
|
||||
import protect from '../../utils/empty-protector';
|
||||
|
||||
export function encodeScriptTags(value) {
|
||||
return value
|
||||
@ -78,19 +79,6 @@ export function loggerToStr(args) {
|
||||
.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(
|
||||
current,
|
||||
entities,
|
||||
@ -285,20 +273,6 @@ export function getMouse(e, [dx, 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 {
|
||||
// isHidden: Boolean,
|
||||
// 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 { Observable, Scheduler } from 'rx';
|
||||
import debug from 'debug';
|
||||
import accepts from 'accepts';
|
||||
import dedent from 'dedent';
|
||||
|
||||
import { ifNoUserSend } from '../utils/middleware';
|
||||
import { cachedMap } from '../utils/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');
|
||||
|
||||
@ -274,40 +278,34 @@ export default function(app) {
|
||||
result,
|
||||
entities: createNameIdMap(entities)
|
||||
}))
|
||||
.map(({
|
||||
result,
|
||||
entities: {
|
||||
challenge: challengeMap,
|
||||
block: blockMap,
|
||||
superBlock: superBlockMap,
|
||||
challengeIdToName
|
||||
}
|
||||
}) => {
|
||||
.map(map => {
|
||||
checkMapData(map);
|
||||
const {
|
||||
entities: { challenge: challengeMap, challengeIdToName }
|
||||
} = map;
|
||||
let finalChallenge;
|
||||
const dashedName = challengeIdToName[user && user.currentChallengeId];
|
||||
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
|
||||
if (!finalChallenge) {
|
||||
finalChallenge = challengeMap[
|
||||
block[
|
||||
superBlockMap[
|
||||
result[0]
|
||||
].blocks[0]
|
||||
].challenges[0]
|
||||
];
|
||||
finalChallenge = getFirstChallenge(map);
|
||||
}
|
||||
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}`;
|
||||
})
|
||||
.subscribe(
|
||||
|
Reference in New Issue
Block a user