diff --git a/backend/package-lock.json b/backend/package-lock.json index 31d92f6..ec32ca8 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -12,6 +12,7 @@ "cheerio": "^1.0.0-rc.12", "cors": "^2.8.5", "dotenv": "^16.3.1", + "expr-eval": "^2.0.2", "express": "^4.18.2", "graphql": "^16.8.1", "graphql-request": "^6.1.0", @@ -2782,6 +2783,12 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/expr-eval": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expr-eval/-/expr-eval-2.0.2.tgz", + "integrity": "sha512-4EMSHGOPSwAfBiibw3ndnP0AvjDWLsMvGOvWEZ2F96IGk0bIVdjQisOHxReSkE13mHcfbuCiXw+G4y0zv6N8Eg==", + "license": "MIT" + }, "node_modules/express": { "version": "4.21.2", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", diff --git a/backend/package.json b/backend/package.json index b96cee4..7c38461 100644 --- a/backend/package.json +++ b/backend/package.json @@ -17,6 +17,7 @@ "cheerio": "^1.0.0-rc.12", "cors": "^2.8.5", "dotenv": "^16.3.1", + "expr-eval": "^2.0.2", "express": "^4.18.2", "graphql": "^16.8.1", "graphql-request": "^6.1.0", diff --git a/backend/src/services/smartPackageManagementService.js b/backend/src/services/smartPackageManagementService.js index 5b598de..b40b404 100644 --- a/backend/src/services/smartPackageManagementService.js +++ b/backend/src/services/smartPackageManagementService.js @@ -1565,15 +1565,11 @@ class SmartPackageManagementService { console.log('🧮 [FORMULA] After substitution:', expr); logger.info('After variable substitution:', { expr, geometry: { length: geometry.length_main, width: geometry.width_main, area: geometry.total_area } }); - // Evaluer matematisk udtryk (sikker måde) - // Tillad kun tal, +, -, *, /, (, ), . - if (!/^[\d\s+\-*/.()]+$/.test(expr)) { - console.log('⚠️ [FORMULA] Invalid expression:', expr); - logger.warn('Invalid formula expression:', { expr, original: formula }); - return 1; // Fallback - } - - const result = eval(expr); + // Evaluer matematisk udtryk via sikker parser + const { Parser } = require('expr-eval'); + const parser = new Parser({}); + const parsed = parser.parse(String(expr || '0')); + const result = Number(parsed.evaluate(variables)) || 0; console.log('✅ [FORMULA] Result:', result); logger.info('Formula result:', { result, expr }); return Math.max(0, result); // Ingen negative værdier diff --git a/backend/unified-server.js b/backend/unified-server.js index 261123f..f7b493c 100644 --- a/backend/unified-server.js +++ b/backend/unified-server.js @@ -6,6 +6,7 @@ const http = require('http'); const { Server } = require('socket.io'); const jwt = require('jsonwebtoken'); const axios = require('axios'); +const { Parser } = require('expr-eval'); // const HistoricalQuoteCalculator = require('./historical-quote-calculator.js'); // Temporarily disabled // Load environment variables FIRST @@ -211,14 +212,23 @@ async function calculateMaterialQuantities(projectId, geometry) { for (const formula of formulas) { try { - // Create safe evaluation context + // Build evaluation context (support both GEOM.xxx and direct vars) const GEOM = { - area_m2: geometry.area_m2 || 0, - vindskede_lbm: geometry.vindskede_lbm || 0 + area_m2: Number(geometry.area_m2) || 0, + vindskede_lbm: Number(geometry.vindskede_lbm) || 0 }; - - // Evaluate formula safely - const calculatedValue = eval(formula.qty_formula); + const vars = { GEOM, ...GEOM }; + + // Evaluate formula using a safe math parser + let calculatedValue = 0; + try { + const parser = new Parser({}); + const expr = parser.parse(String(formula.qty_formula || '0')); + calculatedValue = Number(expr.evaluate(vars)) || 0; + } catch (e) { + console.error(`❌ Formula parse/eval error for ${formula.material_sku}:`, e.message); + calculatedValue = 0; + } const quantity = Math.ceil(calculatedValue * formula.unit_per_formula * formula.waste_factor); if (quantity > 0) {