Skip to content

Instantly share code, notes, and snippets.

@danneu
Forked from joepie91/index.js
Created March 21, 2020 04:44
Show Gist options
  • Save danneu/425f41997d871d4f3a31596393a14656 to your computer and use it in GitHub Desktop.
Save danneu/425f41997d871d4f3a31596393a14656 to your computer and use it in GitHub Desktop.
Breaking CloudFlare's "I'm Under Attack" challenge
'use strict';
const parseExpression = require("./parse-expression");
function findAll(regex, target) {
let results = [], match;
while (match = regex.exec(target)) {
results.push(match);
}
return results;
}
module.exports = function(testcase) {
let result;
let [_, parent, child, initialExpression] = /var s,t,o,p,b,r,e,a,k,i,n,g,f,\s*([a-zA-Z]+)={"([a-zA-Z]+)":([^}]+)};/.exec(testcase);
let modifyingExpressions = findAll(new RegExp(`${parent}\.${child}\s*([*+-])=\s*([^;]+)`, "g"), testcase).map((match) => {
return {
operation: match[1],
expression: match[2]
}
}).map(({operation, expression}) => {
return {
operation,
expression: parseExpression(expression)
}
});
initialExpression = parseExpression(initialExpression);
return {parent, child, initialExpression, modifyingExpressions};
}
'use strict';
const arrayEqual = require("array-equal");
module.exports = function(string) {
return evaluate(parse(string));
}
function evaluateBranch(tree, modifiers) {
let result = tree.map((expression) => {
if (expression.type === "group") {
return evaluateBranch(expression.values, expression.modifiers);
} else if (expression.modifiers.length === 0) {
return ""; // This is a trigger to stringify the previous values
} else if (arrayEqual(["plus"], expression.modifiers)) {
return 0;
} else if (arrayEqual(["negate", "negate"], expression.modifiers)) {
return true;
} else if (arrayEqual(["negate", "plus"], expression.modifiers)) {
return true;
} else if (arrayEqual(["plus", "plus", "negate"], expression.modifiers)) {
return true;
} else if (arrayEqual(["plus", "negate", "negate"], expression.modifiers)) {
return 1;
} else {
throw new Error(`Found unrecognized modifier pattern: ${expression.modifiers}`)
}
}).reduce((combined, value) => {
if (value === "") {
return combined.toString();
} else {
if (value === true) {
value = 1;
}
if (typeof combined === "string") {
return combined + value.toString();
} else {
return combined + value;
}
}
}, 0);
if (modifiers == null) {
return result;
} else {
if (arrayEqual(["plus"], modifiers)) {
return parseInt(result);
} else {
return result;
}
}
}
function evaluate(tree) {
return evaluateBranch(tree);
}
function parse(string) {
let length = string.length;
let byte;
let stateFinishedItem = false;
let modifierStack = [[]];
let itemStack = [[]];
let currentStack = itemStack[0];
let currentModifiers = modifierStack[0];
let stackLevel = 0;
for (let pos = 0; pos < length; pos++) {
byte = string[pos];
switch (byte) {
case "+":
if (pos === 0 || stateFinishedItem === false) {
// Modifier / number-cast
currentModifiers.push("plus");
stateFinishedItem = false;
} else {
// Addition, we don't need to do anything here
}
break;
case "!":
stateFinishedItem = false;
currentModifiers.push("negate");
break;
case "(":
stateFinishedItem = false;
stackLevel++;
itemStack[stackLevel] = currentStack = [];
modifierStack[stackLevel] = currentModifiers = [];
break;
case ")":
if (stackLevel === 0) {
throw new Error(`Encountered ) without matching (`);
}
stackLevel--;
stateFinishedItem = true;
currentStack = itemStack[stackLevel];
currentStack.push({
type: "group",
values: itemStack[stackLevel + 1],
modifiers: modifierStack[stackLevel]
});
currentModifiers = modifierStack[stackLevel] = [];
break;
case "[":
if (string[pos + 1] === "]") {
// Reached the brackets; end of the modifier sequence
currentStack.push({
type: "brackets",
modifiers: currentModifiers
});
currentModifiers = [];
pos += 1; // Skip over the closing bracket
stateFinishedItem = true;
} else {
throw new Error(`Invalid byte found; expected ] but got ${string[pos + 1]}`);
}
}
}
return itemStack[0];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment