cloudflare-esi
Advanced tools
Comparing version 0.0.0-0ee57d19ad4993c1d8d80728084daaa04272edec to 0.0.0-2d059ad9214af4d8c98600e09cb6d2d0200ff69d
@@ -93,3 +93,3 @@ /// <reference types="@cloudflare/workers-types" /> | ||
export declare type customESIVarsFunction = (request: Request) => Promise<customESIVars>; | ||
export declare type fetchFunction = (request: string | Request, requestInitr?: Request | RequestInit | undefined) => Promise<Response>; | ||
export declare type fetchFunction = (request: string | Request) => Promise<Response>; | ||
export declare class esi { | ||
@@ -96,0 +96,0 @@ options: ESIConfig; |
@@ -240,3 +240,3 @@ import { create as createHandleChunk } from "./handleChunk"; | ||
// return a brand new | ||
return [new Request(current.toString(), request), vars]; | ||
return [new Request(current.toString(), request.clone()), vars]; | ||
} | ||
@@ -243,0 +243,0 @@ /** |
@@ -87,3 +87,7 @@ import { tagParser } from "./tagParser"; | ||
} | ||
// All of our ESI regexs | ||
const regexExtractor = /\/(.*?)(?<!\\)\/([a-z]*)/; | ||
const reg_esi_seperator = /(?:'.*?(?<!\\)')|(\|{1,2}|&{1,2}|!(?!=))/g; | ||
const reg_esi_brackets = /(?:'.*?(?<!\\)')|(\(|\))/g; | ||
const reg_esi_condition = /(?:(\d+(?:\.\d+)?)|(?:'(.*?)(?<!\\)'))\s*(!=|==|=~|<=|>=|>|<)\s*(?:(\d+(?:\.\d+)?)|(?:'(.*?)(?<!\\)'))/; | ||
/** | ||
@@ -108,95 +112,182 @@ * Evaluates esi Vars within when tag conditional statements | ||
/** | ||
* Takes a condition string and turns it into javascript to be ran | ||
* if the condition is invalid returns null otherwise the compiled string | ||
* Takes a condition string and splits it into its two sides and operator | ||
* passes that to the tester and returns the result | ||
* | ||
* @param {string} condition conditional string to build out | ||
* @returns {null[] | [boolean, string]} valid condition compiled or null | ||
* @param {string} condition conditional string to split | ||
* @returns {boolean} condition result | ||
*/ | ||
async function _esi_condition_lexer(condition) { | ||
const reg_esi_condition = /(\d+(?:\.\d+)?)|(?:'(.*?)(?<!\\)')|(!=|!|\|{1,2}|&{1,2}|={2}|=~|\(|\)|<=|>=|>|<)/g; | ||
const op_replacements = { | ||
"!=": "!==", | ||
"|": " || ", | ||
"&": " && ", | ||
"||": " || ", | ||
"&&": " && ", | ||
"!": " ! ", | ||
"|": "||", | ||
"&": "&&", | ||
"||": "||", | ||
"&&": "&&", | ||
"!": "!", | ||
}; | ||
const lexer_rules = { | ||
number: { | ||
nil: true, | ||
operator: true, | ||
}, | ||
string: { | ||
nil: true, | ||
operator: true, | ||
}, | ||
operator: { | ||
nil: true, | ||
number: true, | ||
string: true, | ||
operator: true, | ||
}, | ||
const tokensSplit = condition.match(reg_esi_condition); | ||
if (tokensSplit == null) { | ||
return false; | ||
} | ||
const left = tokensSplit[1] || tokensSplit[2]; | ||
const op = op_replacements[tokensSplit[3]] || tokensSplit[3]; | ||
const right = tokensSplit[4] || tokensSplit[5]; | ||
return esiConditionTester(left, right, op); | ||
} | ||
/** | ||
* Takes a condition broken down into an a, b & operator and tests the data | ||
* returns the result | ||
* | ||
* @param {string | number} left conditional left | ||
* @param {string | number} right conditional right | ||
* @param {string} operator operator to compare | ||
* @returns {boolean} condition result | ||
*/ | ||
function esiConditionTester(left, right, operator) { | ||
switch (operator) { | ||
case "==": | ||
case "===": | ||
return left === right; | ||
case "!==": | ||
return left !== right; | ||
case ">=": | ||
return left >= right; | ||
case "<=": | ||
return left <= right; | ||
case "<": | ||
return left < right; | ||
case ">": | ||
return left > right; | ||
case "=~": { | ||
const regex = right.match(regexExtractor); | ||
if (!regex) | ||
return false; | ||
// Bloody javascript! | ||
// Gotta cleanup some escaping here | ||
// Only have to do it for regex'd strings | ||
// As normal comparison strings should be escaped the same (need to be to be equal) | ||
left = left.replace(/\\"/g, '"'); | ||
left = left.replace(/\\'/g, "'"); | ||
left = left.replace(/\\\\/g, "\\"); | ||
const reg = new RegExp(regex[1], regex[2]); | ||
return reg.test(left); | ||
} | ||
} | ||
return false; | ||
} | ||
/** | ||
* Takes a condition string and splits it into parts splitting along a seperator | ||
* seperators are logical seperators ie `|` or `&` | ||
* passes the splits along to ${_esi_condition_lexer} and then | ||
* returns the result of the condition after comparing the splits | ||
* against their logical seperators | ||
* | ||
* @param {string} condition conditional string to split | ||
* @returns {boolean} condition result | ||
*/ | ||
async function esi_seperator_splitter(condition) { | ||
let startingIndex = 0; | ||
let negatorySeperator = false; | ||
let prevSeperator = ""; | ||
let valid = null; | ||
const handleString = async function (str) { | ||
if (str == "false" || str == "true") { | ||
return str === "true"; | ||
} | ||
else { | ||
return await _esi_condition_lexer(str); | ||
} | ||
}; | ||
const tokens = []; | ||
let prev_type = "nil"; | ||
let expectingPattern = false; | ||
const tokensSplit = condition.matchAll(reg_esi_condition); | ||
const validityCheck = function (res, seperator) { | ||
if (negatorySeperator) { | ||
res = !res; | ||
negatorySeperator = !negatorySeperator; | ||
} | ||
if (valid == null) { | ||
return res; | ||
} | ||
switch (seperator) { | ||
case "&": | ||
case "&&": | ||
return valid && res; | ||
case "||": | ||
case "|": | ||
return valid || res; | ||
} | ||
return valid; | ||
}; | ||
const tokensSplit = condition.matchAll(reg_esi_seperator); | ||
for (const token of tokensSplit) { | ||
const number = token[1]; | ||
const string = token[2]; | ||
const operator = token[3]; | ||
let token_type = "nil"; | ||
if (number) { | ||
token_type = "number"; | ||
tokens.push(number); | ||
if (!token[1]) | ||
continue; | ||
const seperator = token[1]; | ||
// Negatory seperator! | ||
if (seperator == "!") { | ||
negatorySeperator = !negatorySeperator; | ||
continue; | ||
} | ||
else if (string) { | ||
token_type = "string"; | ||
if (expectingPattern) { | ||
const regex = string.match(regexExtractor); | ||
if (!regex) { | ||
return null; | ||
} | ||
else { | ||
const pattern = regex[1]; | ||
const options = regex[2]; | ||
const cmpString = tokens.pop(); | ||
// tokens.push(`(${cmpString}.search(/${pattern}/${options}) !== -1)`) | ||
tokens.push(`/${pattern}/${options}.test(${cmpString})`); | ||
} | ||
expectingPattern = false; | ||
} | ||
else { | ||
tokens.push(`'${string}'`); | ||
} | ||
const conditionBefore = condition | ||
.substring(startingIndex, token.index) | ||
.trim(); | ||
const res = await handleString(conditionBefore); | ||
valid = validityCheck(res, prevSeperator); | ||
prevSeperator = seperator; | ||
// Move onto the next one | ||
startingIndex = token.index + seperator.length; | ||
} | ||
const finalRes = await handleString(condition.substring(startingIndex).trim()); | ||
valid = validityCheck(finalRes, prevSeperator); | ||
return valid; | ||
} | ||
/** | ||
* Takes a condition string and splits it into seperate parts based off | ||
* brackets amd passes the splits along to ${esi_seperator_splitter} and then | ||
* returns the result of the condition | ||
* | ||
* @param {string} condition conditional string to split | ||
* @returns {boolean} condition result | ||
*/ | ||
async function esi_bracket_splitter(condition) { | ||
let parsedPoint = 0; | ||
let startingIndex = 0; | ||
let endIndex = -1; | ||
let depth = 0; | ||
const fullExpression = []; | ||
const tokensSplit = condition.matchAll(reg_esi_brackets); | ||
for (const token of tokensSplit) { | ||
if (!token[1]) | ||
continue; | ||
const bracket = token[1]; | ||
if (bracket == "(") { | ||
if (depth == 0) | ||
startingIndex = token.index; | ||
depth = depth + 1; | ||
} | ||
else if (operator) { | ||
token_type = "operator"; | ||
if (operator == "=~") { | ||
if (prev_type == "operator") { | ||
return null; | ||
} | ||
else { | ||
expectingPattern = true; | ||
} | ||
if (bracket == ")") { | ||
// bail out if its invalid depth | ||
if (depth == 0) | ||
return false; | ||
depth = depth - 1; | ||
// Right we have a full bracketed set | ||
if (depth == 0) { | ||
endIndex = token.index; | ||
fullExpression.push(condition.substring(parsedPoint, startingIndex)); | ||
const conditionBracketed = condition.substring(startingIndex + 1, endIndex); | ||
// Loop it back to see if there is another bracket inside | ||
const bracketResult = await esi_bracket_splitter(conditionBracketed); | ||
fullExpression.push(bracketResult.toString()); | ||
// Know were we are up too | ||
parsedPoint = endIndex + 1; | ||
} | ||
else { | ||
tokens.push(op_replacements[operator] || operator); | ||
} | ||
} | ||
if (prev_type !== "nil") { | ||
if (!lexer_rules[prev_type][token_type]) { | ||
return null; | ||
} | ||
} | ||
// Derefence it | ||
prev_type = `${token_type}`; | ||
} | ||
// If we havent got a regex yet but we're expecting one | ||
// fail. | ||
if (expectingPattern) { | ||
return null; | ||
// If we didnt have any then push it all along | ||
// Otherwise push the leftovers and return | ||
if (endIndex == -1) { | ||
return await esi_seperator_splitter(condition); | ||
} | ||
return tokens.join(" "); | ||
else { | ||
fullExpression.push(condition.substring(endIndex + 1)); | ||
return await esi_seperator_splitter(fullExpression.join("")); | ||
} | ||
} | ||
@@ -213,14 +304,4 @@ /** | ||
condition = replace_vars(esiData, condition, esi_eval_var_in_when_tag); | ||
const compiledCondition = await _esi_condition_lexer(condition); | ||
if (!compiledCondition) { | ||
return false; | ||
} | ||
try { | ||
const ret = Function(`"use strict"; return( ${compiledCondition} )`)(); | ||
return ret; | ||
} | ||
catch (err) { | ||
return false; | ||
} | ||
return await esi_bracket_splitter(condition); | ||
} | ||
//# sourceMappingURL=processConditionals.js.map |
{ | ||
"name": "cloudflare-esi", | ||
"version": "0.0.0-0ee57d19ad4993c1d8d80728084daaa04272edec", | ||
"version": "0.0.0-2d059ad9214af4d8c98600e09cb6d2d0200ff69d", | ||
"repository": "cdloh/cloudflare-esi", | ||
@@ -45,3 +45,9 @@ "description": "ESI Parser built to run in Cloudflare workers", | ||
"rules": { | ||
"jsdoc/no-undefined-types": 0 | ||
"jsdoc/no-undefined-types": 0, | ||
"no-new-func": [ | ||
"error" | ||
], | ||
"no-eval": [ | ||
"error" | ||
] | ||
} | ||
@@ -48,0 +54,0 @@ }, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
101607
1477
1