#javascript #formula #eval
#javascript #формула #eval
Вопрос:
Я работаю над веб-приложением, в котором администратор может определять и адаптировать формулы, которые необходимо будет оценивать на основе входных значений (чисел), предоставляемых конечными пользователями.
Для наглядности, вот сокращенный пример того, что я бы, кроме:
const obj = {
type: "External wall in contact with the ground",
layer: {
base: {
type: "Reinforced concrete (reinforcement 5 vol.%)",
thickness: 100, // <-- user value
lambda: 2.3, // <-- user value
_r: "{{thickness}}/{{lambda}}/1000", // <-- admin defined
r: 0
},
waterproofing: {
type: "Bitumen sheets (single layer)",
share: 1, // <-- user value
_r: "{{share}}", // <-- admin defined
r: 0,
},
insulation: {
type: "XPS",
thickness: 100, // <-- user value
share: 1, // <-- user value
lambda: 0.040, // <-- user value
_r: "{{thickness}}*{{share}}/{{lambda}}/1000", // <-- admin defined
r: 0
}
}
}
Object.entries(obj.layer).forEach(([key, object]) => {
var formula = object._r
Object.keys(object).forEach(k =>
formula = formula.replace(`{{${k}}}`, object[k])
)
obj.layer[key].r = eval(formula)
})
console.log(obj)
Это _r
формула, определенная администратором. Это значения, {{value}}
предоставленные конечными пользователями.
Цикл проходит через obj.layer
свойства, чтобы оценить формулу и сохранить ответ r
.
Результатом будет этот объект:
{
type: 'External wall in contact with the ground',
layer: {
base: {
type: 'Reinforced concrete (reinforcement 5 vol.%)',
thickness: 100,
lambda: 2.3,
_r: '{{thickness}}/{{lambda}}/1000',
r: 0.043478260869565216
},
waterproofing: {
type: 'Bitumen sheets (single layer)',
share: 1,
_r: '{{share}}',
r: 1
},
insulation: {
type: 'XPS',
thickness: 100,
share: 1,
lambda: 0.04,
_r: '{{thickness}}*{{share}}/{{lambda}}/1000',
r: 2.5
}
}
}
Давайте пропустим тот факт, что я не проверяю структуру объекта и не проверяю, доступны ли все значения.
Я знаю eval()
, что это считается «опасным». Не очень хорошей альтернативой было бы Function()
. Тем не менее, не идеально.
Пока я вижу 3 возможности:
- Только администратор может изменять формулы. Таким образом, риск выполнения вредоносного кода очень низок. Что мне нужно, так это проверить / очистить значения (что-то вроде
isFloat()
), и на этом все. - Используя
mathjs
библиотеку, которая предлагает приятнуюevaluate()
функцию:
const node2 = math.parse('x^a')
const code2 = node2.compile()
let scope = {
x: 3,
a: 2
}
code2.evaluate(scope) // 9
- Используйте генератор синтаксического анализа, например http://zaa.ch/jison /, но это кажется излишним для того, что я хочу сделать..
Честно говоря, я считаю, что использование eval()
в моем конкретном случае оправдано: динамические формулы с динамическими значениями. Я мог бы использовать внешнюю библиотеку, например mathjs
, но я чувствую, что она мне не нужна для таких простых операций.
Я бы очень хотел получить вашу идею по этому вопросу и услышать ваше предложение, если таковое имеется!
PS: Да, вопрос уже был задан. Хотя самые похожие вопросы, которые я нашел, были заданы (и на которые были даны ответы) несколько лет назад. Я хотел бы получить свежий вклад в этот вопрос.
Основываясь на ответе @Sleavely и дальнейшем чтении по теме, mathjs
представляется наиболее разумным решением для принятия
Комментарии:
1. как бы вы помешали пользователям вводить проблемный ввод? xkcd.com/327 вы будете проводить дезинфекцию?
2. Сторонние администраторы могут быть единственными, кто может редактировать код, но где он будет выполняться на самом деле? В вашем бэкэнде? На компьютерах посетителей? Я настоятельно рекомендую вам избегать eval(), чтобы вас не обвинили в том, что посетители биткойн-майнера в конечном итоге получают от вашего приложения. Имея это в виду, я думаю, что вы на правильном пути со своим 2-м вариантом. Оценка формулы по набору предопределенных переменных кажется мне достаточно безопасной. Похоже
mathjs
, активно избегает eval(): github.com/josdejong/mathjs/blob/master/docs/expressions /…3. Могу я предложить mathjs.org/index.html вместо того, чтобы использовать ванильный eval().
4. @malarres Да, я бы, конечно, очистил ввод. Входные данные должны быть только с плавающей запятой.
5. @Sleavely Спасибо за ссылки. Тбх, я думал, что mathjs работает
eval()
за сценой. Вот почему мне было интересно, зачем добавлять библиотеку, если я могу сделать «то же самое». У меня такое чувствоmathjs
, что это среднее решение между плохимeval()
и сложным (?)jison
решением
Ответ №1:
На основе ответа @Sleavely (см. Комментарий, приведенный Ниже) и дальнейшего чтения по теме, mathjs
представляется наиболее разумным решением:
Сторонние администраторы могут быть единственными, кто может редактировать код, но где он будет выполняться на самом деле? В вашем бэкэнде? На компьютерах посетителей? Я настоятельно рекомендую вам избегать eval(), чтобы вас не обвинили в том, что посетители биткойн-майнера в конечном итоге получают от вашего приложения. Имея это в виду, я думаю, что вы на правильном пути со своим 2-м вариантом. Оценка формулы по набору предопределенных переменных кажется мне достаточно безопасной. Похоже, mathjs активно избегает eval(): https://github.com/josdejong/mathjs/blob/master/docs/expressions/security.md