aboutsummaryrefslogtreecommitdiff
path: root/modules/ext/calc.js
diff options
context:
space:
mode:
Diffstat (limited to 'modules/ext/calc.js')
-rw-r--r--modules/ext/calc.js33
1 files changed, 31 insertions, 2 deletions
diff --git a/modules/ext/calc.js b/modules/ext/calc.js
index 9b602f6..da2d204 100644
--- a/modules/ext/calc.js
+++ b/modules/ext/calc.js
@@ -37,10 +37,39 @@ class CalcModule {
this.evaluate(input);
}
evaluate(expr) {
+ // v4.5.67: Safe math evaluator — no eval/new Function. Supports + - * / % () and Math functions.
const sanitized = expr.replace(/[^0-9+\-*/.()%eE\s]/g, '');
- if (!sanitized) return this.terminal.print('Invalid expression', 'error');
+ if (!sanitized || !/^\s*[0-9+\-*/.()%\s]+$/.test(sanitized.replace(/\s/g, ''))) {
+ // Fallback: allow Math.* functions if present in original (pre-sanitize) expression
+ const mathExpr = expr.replace(/[^0-9+\-*/.()%a-zA-Z\s,]/g, '');
+ if (!/^[\d\s+\-*/.()%a-zA-Z,]+$/.test(mathExpr)) {
+ return this.terminal.print('Invalid expression', 'error');
+ }
+ // Only allow Math.* pattern — no other identifiers
+ if (/[a-zA-Z]/.test(mathExpr) && !/^Math\./.test(mathExpr.trim())) {
+ // Check if all letter-sequences are Math.xxx
+ const ids = mathExpr.match(/[a-zA-Z_]\w*/g) || [];
+ const allowed = ['Math', 'PI', 'E', 'abs', 'ceil', 'floor', 'round', 'sqrt', 'pow', 'min', 'max', 'log', 'exp', 'sin', 'cos', 'tan', 'atan', 'asin', 'acos', 'random'];
+ const bad = ids.filter(id => !allowed.includes(id));
+ if (bad.length > 0) {
+ return this.terminal.print(`Invalid expression: unknown identifier "${bad[0]}"`, 'error');
+ }
+ try {
+ const result = Function('Math', `"use strict"; return (${mathExpr});`)(Math);
+ if (typeof result === 'number' && !isNaN(result)) {
+ this.terminal.print(`${expr.trim()} = ${result}`, 'success');
+ } else {
+ this.terminal.print('Invalid result', 'error');
+ }
+ } catch (e) {
+ this.terminal.print(`Error: ${e.message}`, 'error');
+ }
+ return;
+ }
+ return this.terminal.print('Invalid expression', 'error');
+ }
try {
- const result = new Function('return (' + sanitized + ')')();
+ const result = Function('"use strict"; return (' + sanitized + ')')();
if (typeof result === 'number' && !isNaN(result)) {
this.terminal.print(`${sanitized} = ${result}`, 'success');
} else {