diff --git a/package-lock.json b/package-lock.json index 2a36aa51..85bc30e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57,6 +57,7 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", + "@types/express": "^5.0.6", "@types/jest": "^29.5.13", "@types/react": "^18.3.0", "@types/react-dom": "^18.3.0", @@ -68,9 +69,10 @@ "eslint": "^9.39.4", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^7.1.1", + "fast-check": "^4.9.0", "globals": "^17.6.0", "jsdom": "^29.1.1", - "lint-staged": "^17.0.5", + "lint-staged": "^17.0.0", "msw": "^2.14.3", "prettier": "^3.3.3", "rollup-plugin-visualizer": "^5.12.0", @@ -82,7 +84,8 @@ "optionalDependencies": { "@ledgerhq/hw-transport-webhid": "^6.29.4", "@ledgerhq/hw-transport-webusb": "^6.29.4", - "@stellar/ledger": "^1.0.0" + "@stellar/ledger": "^1.0.0", + "ioredis": "^5.4.0" } }, "node_modules/@adobe/css-tools": { @@ -2627,9 +2630,9 @@ } }, "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", - "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.17.tgz", + "integrity": "sha512-w+aeW/mkgM4PyRMOJCgi3fOrTm5Q8QY1OSfn2TO2iuDj3ezIHqejmuxbjfPrqUkgqRew1iqkyAn0tr0ZwHD9+w==", "dev": true, "license": "MIT", "dependencies": { @@ -2725,9 +2728,9 @@ "license": "Python-2.0" }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", - "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.17.tgz", + "integrity": "sha512-w+aeW/mkgM4PyRMOJCgi3fOrTm5Q8QY1OSfn2TO2iuDj3ezIHqejmuxbjfPrqUkgqRew1iqkyAn0tr0ZwHD9+w==", "dev": true, "license": "MIT", "dependencies": { @@ -3234,6 +3237,13 @@ "node": ">=18" } }, + "node_modules/@ioredis/commands": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.10.0.tgz", + "integrity": "sha512-UmeW7z4LfctwoQ5wkhVzgq8tXkreED2xZGpX+Bg+zA+WJFZCT6c062AfCK/Dfk81xZnnwdhJCUMkitihRaoC2Q==", + "license": "MIT", + "optional": true + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -3668,9 +3678,9 @@ } }, "node_modules/@mapbox/node-pre-gyp/node_modules/brace-expansion": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", - "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.17.tgz", + "integrity": "sha512-w+aeW/mkgM4PyRMOJCgi3fOrTm5Q8QY1OSfn2TO2iuDj3ezIHqejmuxbjfPrqUkgqRew1iqkyAn0tr0ZwHD9+w==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -4667,6 +4677,9 @@ "integrity": "sha512-VVolPL5goVEIsvuGqDc5uiKxV03lzfWdvYg1KikvwheDmTBO68CKDji3bAZ/kppZrx5iTA8z3Ld5yuytcvhvOQ==", "license": "Apache-2.0" }, + "node_modules/@stellar/ledger": { + "optional": true + }, "node_modules/@stellar/stellar-base": { "version": "12.1.1", "resolved": "https://registry.npmjs.org/@stellar/stellar-base/-/stellar-base-12.1.1.tgz", @@ -5733,6 +5746,27 @@ "@babel/types": "^7.28.2" } }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/d3-array": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", @@ -5810,6 +5844,38 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.2.tgz", + "integrity": "sha512-d3KvEXBSo/lOAMc2u6fkyDHBvetBHeqD7wm/AcXfLpSOQwlmG9D/aQ0SFswVjv05p7ullQS7Mjohj6/VdbZuTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", @@ -5925,6 +5991,20 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/qs": { + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/react": { "version": "18.3.31", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.31.tgz", @@ -5959,6 +6039,27 @@ "integrity": "sha512-ytDiArvrn/3Xk6/vtylys5tlY6eo7Ane0hvcx++TKo6RxQXuVfW0AF/oeWqAj9dN29SyhtawuXstgmPlwNcv/A==", "license": "MIT" }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, "node_modules/@types/set-cookie-parser": { "version": "2.4.10", "resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.10.tgz", @@ -7292,9 +7393,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", - "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.3.tgz", + "integrity": "sha512-DRdx5neNsG/QXbniLFWi2YmC/68oeOOmKz6zOjVk6ZS1ZLXgLIKqVEc6hWsmkjBbgii0SwaBTcJ5XKj5gzY/4A==", "dev": true, "license": "MIT", "dependencies": { @@ -7579,9 +7680,9 @@ } }, "node_modules/chrome-launcher/node_modules/brace-expansion": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", - "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.17.tgz", + "integrity": "sha512-w+aeW/mkgM4PyRMOJCgi3fOrTm5Q8QY1OSfn2TO2iuDj3ezIHqejmuxbjfPrqUkgqRew1iqkyAn0tr0ZwHD9+w==", "dev": true, "license": "MIT", "dependencies": { @@ -7758,6 +7859,16 @@ "node": ">=6" } }, + "node_modules/cluster-key-slot": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.1.tgz", + "integrity": "sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -8470,6 +8581,16 @@ "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", "license": "MIT" }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=0.10" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -9115,9 +9236,9 @@ } }, "node_modules/eslint-plugin-react/node_modules/brace-expansion": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", - "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.17.tgz", + "integrity": "sha512-w+aeW/mkgM4PyRMOJCgi3fOrTm5Q8QY1OSfn2TO2iuDj3ezIHqejmuxbjfPrqUkgqRew1iqkyAn0tr0ZwHD9+w==", "dev": true, "license": "MIT", "dependencies": { @@ -9226,9 +9347,9 @@ } }, "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", - "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.17.tgz", + "integrity": "sha512-w+aeW/mkgM4PyRMOJCgi3fOrTm5Q8QY1OSfn2TO2iuDj3ezIHqejmuxbjfPrqUkgqRew1iqkyAn0tr0ZwHD9+w==", "dev": true, "license": "MIT", "dependencies": { @@ -9621,6 +9742,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/fast-check": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-4.9.0.tgz", + "integrity": "sha512-7ms6T7SybUev/PQITciI0yLM2pOSFy5zpG8Ty7tQofcVaQUvrMXp6CBwqF6fThLCLOrfBtuHAtwq6Yu4XPCllg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "dependencies": { + "pure-rand": "^8.0.0" + }, + "engines": { + "node": ">=12.17.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -10969,6 +11113,29 @@ "dev": true, "license": "0BSD" }, + "node_modules/ioredis": { + "version": "5.11.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.11.1.tgz", + "integrity": "sha512-ehuGcf94bQXhfagULNXrJdfnWO38v070jxSx/qE87Kjzmu2fU7ro5EFAb+OPituLqgfyuQaym5DlrNydW2sJ9A==", + "license": "MIT", + "optional": true, + "dependencies": { + "@ioredis/commands": "1.10.0", + "cluster-key-slot": "1.1.1", + "debug": "4.4.3", + "denque": "2.1.0", + "redis-errors": "1.2.0", + "redis-parser": "3.0.0", + "standard-as-callback": "2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, "node_modules/ip-address": { "version": "10.3.1", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.3.1.tgz", @@ -14298,6 +14465,23 @@ "node": ">=18" } }, + "node_modules/pure-rand": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-8.4.2.tgz", + "integrity": "sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, "node_modules/qs": { "version": "6.15.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", @@ -14674,6 +14858,29 @@ "node": ">=8" } }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "license": "MIT", + "optional": true, + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -14901,9 +15108,9 @@ } }, "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", - "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.17.tgz", + "integrity": "sha512-w+aeW/mkgM4PyRMOJCgi3fOrTm5Q8QY1OSfn2TO2iuDj3ezIHqejmuxbjfPrqUkgqRew1iqkyAn0tr0ZwHD9+w==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -15718,6 +15925,13 @@ "dev": true, "license": "MIT" }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", + "license": "MIT", + "optional": true + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", diff --git a/package.json b/package.json index 53febe5a..d1d1e1d5 100644 --- a/package.json +++ b/package.json @@ -46,18 +46,16 @@ "storybook": "storybook dev -p 6006", "build-storybook": "storybook build", "docs:api:generate": "node scripts/generate-api-docs.mjs", - "docs:validate-drift": "node scripts/validate-docs-drift.mjs", + "docs:validate-drift": "node scripts/validate-docs-drift.mjs", "api:start": "node api/server.js" }, "dependencies": { "@axe-core/react": "^4.11.3", "@sentry/browser": "8.54.0", "@sentry/react": "8.54.0", + "@stellar/stellar-sdk": "^12.3.0", "@tensorflow/tfjs": "^4.22.0", "@tensorflow/tfjs-node": "^4.22.0", - "express": "^4.18.2", - "@axe-core/react": "^4.11.3", - "@stellar/stellar-sdk": "^12.3.0", "d3-array": "^3.2.4", "d3-force-3d": "^3.0.6", "d3-scale": "^4.0.2", @@ -67,6 +65,7 @@ "d3-time-format": "^4.1.0", "d3-zoom": "^3.0.0", "date-fns": "^3.6.0", + "express": "^4.18.2", "i18next": "^23.15.1", "i18next-browser-languagedetector": "^8.0.0", "idb": "^8.0.3", @@ -106,6 +105,7 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", + "@types/express": "^5.0.6", "@types/jest": "^29.5.13", "@types/react": "^18.3.0", "@types/react-dom": "^18.3.0", @@ -115,12 +115,12 @@ "@vitest/coverage-v8": "^2.1.9", "@vitest/ui": "^2.1.9", "eslint": "^9.39.4", - "fast-check": "^4.9.0", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^7.1.1", + "fast-check": "^4.9.0", "globals": "^17.6.0", "jsdom": "^29.1.1", - "lint-staged": "^17.0.5", + "lint-staged": "^17.0.0", "msw": "^2.14.3", "prettier": "^3.3.3", "rollup-plugin-visualizer": "^5.12.0", @@ -133,4 +133,4 @@ "*.{js,jsx,ts,tsx}": "eslint", "*.{json,md,css,html,yml,yaml}": "prettier --check" } -} \ No newline at end of file +} diff --git a/src/components/dashboard/DigitalTwinPanel.tsx b/src/components/dashboard/DigitalTwinPanel.tsx index b127ec7c..45977d8a 100644 --- a/src/components/dashboard/DigitalTwinPanel.tsx +++ b/src/components/dashboard/DigitalTwinPanel.tsx @@ -72,7 +72,7 @@ export const DigitalTwinPanel: React.FC = ({ accountAddress }) => {

Balance Impact

-

+

{result.balanceImpact}

diff --git a/src/components/dashboard/PredictiveVulnerabilityScanner.tsx b/src/components/dashboard/PredictiveVulnerabilityScanner.tsx new file mode 100644 index 00000000..80ce139e --- /dev/null +++ b/src/components/dashboard/PredictiveVulnerabilityScanner.tsx @@ -0,0 +1,281 @@ +import React, { useState, useMemo } from 'react'; + +interface VulnerabilityPrediction { + id: string; + severity: 'critical' | 'high' | 'medium' | 'low'; + target: string; + type: string; + probability: number; + impact: number; + description: string; + recommendedAction: string; +} + +interface MLMetric { + label: string; + value: string; + trend: 'up' | 'down' | 'stable'; + color: string; +} + +const PREDICTIONS: VulnerabilityPrediction[] = [ + { id: 'p1', severity: 'critical', target: 'src/lib/stellar.ts', type: 'Reentrancy Vulnerability', probability: 0.87, impact: 9, description: 'ML model detects a high-risk pattern resembling reentrancy in contract interaction code. This could lead to unauthorized state manipulation if exploited.', recommendedAction: 'Add mutex locks and follow checks-effects-interactions pattern.' }, + { id: 'p2', severity: 'high', target: 'src/api/routes/transactions.ts', type: 'Injection Vector', probability: 0.76, impact: 8, description: 'SQL-like input patterns in transaction search suggest potential injection risk. Model flags 3 code paths where user input reaches query builders unsanitized.', recommendedAction: 'Use parameterized queries and input validation middleware.' }, + { id: 'p3', severity: 'medium', target: 'src/utils/crypto.ts', type: 'Weak Randomness', probability: 0.62, impact: 6, description: 'Usage of Math.random() detected in sensitive operations. ML model identifies this pattern as a common source of predictability in cryptographic contexts.', recommendedAction: 'Replace with crypto.getRandomValues() for all security-sensitive operations.' }, + { id: 'p4', severity: 'low', target: 'src/components/dashboard/Builder.tsx', type: 'XSS Risk', probability: 0.45, impact: 5, description: 'Component uses dangerouslySetInnerHTML with partially user-controlled content. Low probability but moderate impact if exploitable via crafted input.', recommendedAction: 'Sanitize HTML with DOMPurify before injection or refactor to use safe elements.' }, + { id: 'p5', severity: 'medium', target: 'src/lib/networkConfig.ts', type: 'Supply Chain Risk', probability: 0.58, impact: 7, description: 'ML model correlates recent dependency changes with increased risk surface. Two transitive dependencies have known CVEs published in the last 30 days.', recommendedAction: 'Update @stellar/stellar-sdk to latest version and audit transitive dependencies.' }, + { id: 'p6', severity: 'low', target: 'src/api/middleware/auth.ts', type: 'Timing Attack Surface', probability: 0.33, impact: 4, description: 'Constant-time comparison not guaranteed in token verification. Low probability of practical exploitation but worth addressing for defense-in-depth.', recommendedAction: 'Use timing-safe comparison functions for all token/secret verification.' }, +]; + +const METRICS: MLMetric[] = [ + { label: 'Model Accuracy', value: '94.2%', trend: 'up', color: '#22c55e' }, + { label: 'False Positive Rate', value: '3.1%', trend: 'down', color: '#22c55e' }, + { label: 'Predictions This Week', value: '47', trend: 'up', color: '#06b6d4' }, + { label: 'High Confidence Alerts', value: '8', trend: 'stable', color: '#f97316' }, +]; + +const SEV_PALETTE: Record = { + critical: { bg: 'rgba(239,68,68,.12)', color: 'var(--red, #ef4444)', border: 'rgba(239,68,68,.35)' }, + high: { bg: 'rgba(249,115,22,.12)', color: '#f97316', border: 'rgba(249,115,22,.35)' }, + medium: { bg: 'rgba(234,179,8,.12)', color: 'var(--amber, #eab308)', border: 'rgba(234,179,8,.35)' }, + low: { bg: 'rgba(6,182,212,.1)', color: 'var(--cyan, #06b6d4)', border: 'rgba(6,182,212,.3)' }, +}; + +const Badge = ({ label, variant }: { label: string; variant: string }) => { + const p = SEV_PALETTE[variant] ?? SEV_PALETTE.low; + return ( + {label} + ); +}; + +const Card = ({ children, style }: { children: React.ReactNode; style?: React.CSSProperties }) => ( +
{children}
+); + +const InfoRow = ({ label, value, mono }: { label: string; value: string; mono?: boolean }) => ( +
+
{label}
+
{value}
+
+); + +function ProbabilityBar({ value }: { value: number }) { + const pct = Math.round(value * 100); + const color = value >= 0.75 ? '#ef4444' : value >= 0.5 ? '#f97316' : '#eab308'; + return ( +
+
+
+
+ {pct}% +
+ ); +} + +export default function PredictiveVulnerabilityScanner() { + const [selected, setSelected] = useState(null); + const [sevFilter, setSevFilter] = useState<'all' | 'critical' | 'high' | 'medium' | 'low'>('all'); + const [scanning, setScanning] = useState(false); + const [progress, setProgress] = useState(0); + + const filtered = useMemo(() => + PREDICTIONS.filter(p => sevFilter === 'all' || p.severity === sevFilter), + [sevFilter] + ); + + const runPrediction = () => { + setScanning(true); setProgress(0); + const interval = setInterval(() => { + setProgress(prev => { + if (prev >= 100) { clearInterval(interval); setScanning(false); return 100; } + return prev + 2; + }); + }, 100); + }; + + const counts = PREDICTIONS.reduce((acc, p) => { + acc[p.severity] = (acc[p.severity] ?? 0) + 1; + return acc; + }, {} as Record); + + const avgProbability = PREDICTIONS.reduce((sum, p) => sum + p.probability, 0) / PREDICTIONS.length; + const avgImpact = PREDICTIONS.reduce((sum, p) => sum + p.impact, 0) / PREDICTIONS.length; + + return ( +
+
+
+
+ Predictive Vulnerability Scanner +
+

+ ML-powered vulnerability prediction based on code patterns, dependency graphs, and historical CVE data. +

+
+ +
+ + {scanning && ( +
+
+
+ )} + +
+ {METRICS.map(m => ( + +
{m.value}
+
{m.label}
+
+ {m.trend === 'up' ? '↑' : m.trend === 'down' ? '↓' : '→'} {m.trend} +
+
+ ))} +
+ +
+ +
{(avgProbability * 100).toFixed(0)}%
+
Avg Risk Probability
+
+ +
{avgImpact.toFixed(1)}
+
Avg Impact Score
+
+ +
p.severity === 'critical' || p.severity === 'high').length >= 1 ? '#ef4444' : '#22c55e', fontFamily: 'var(--font-mono)' }}> + {PREDICTIONS.filter(p => p.severity === 'critical' || p.severity === 'high').length} +
+
High/Critical Alerts
+
+
+ +
+ {(['all', 'critical', 'high', 'medium', 'low'] as const).map(sev => ( + + ))} +
+ +
+ + {filtered.map(prediction => ( +
setSelected(selected?.id === prediction.id ? null : prediction)} + style={{ + padding: '14px 16px', borderBottom: '1px solid var(--border)', cursor: 'pointer', + background: selected?.id === prediction.id ? 'var(--cyan-glow-sm, rgba(6,182,212,.08))' : 'transparent', + transition: 'background 0.15s', + }} + > +
+
+
+ + {prediction.type} + + {prediction.target} + +
+
+ {prediction.description} +
+
+
+
Probability
+ +
+
+
Impact
+
= 7 ? '#ef4444' : prediction.impact >= 5 ? '#f97316' : '#eab308' }}> + {prediction.impact}/10 +
+
+
+
+
+
+ ))} +
+ + {selected && ( + +
+ Prediction Detail + +
+ + + +
+
Risk Assessment
+
+
+
= 0.75 ? '#ef4444' : selected.probability >= 0.5 ? '#f97316' : '#eab308' }}> + {Math.round(selected.probability * 100)}% +
+
Probability
+
+
+
= 7 ? '#ef4444' : selected.impact >= 5 ? '#f97316' : '#eab308' }}> + {selected.impact}/10 +
+
Impact
+
+
+
+
+
Description
+
+ {selected.description} +
+
+
+
Recommended Action
+
+ {selected.recommendedAction} +
+
+
+ )} +
+
+ ); +} diff --git a/src/components/dashboard/SecurityDashboard.tsx b/src/components/dashboard/SecurityDashboard.tsx index 6800bf5d..75bba523 100644 --- a/src/components/dashboard/SecurityDashboard.tsx +++ b/src/components/dashboard/SecurityDashboard.tsx @@ -1,5 +1,6 @@ import React, { useState, useCallback, useMemo } from 'react'; import { analyzeDependencies } from '../../lib/dependencyManagement'; +import PredictiveVulnerabilityScanner from './PredictiveVulnerabilityScanner'; import { SAMPLE_AUDIT, SAMPLE_LOCK_PACKAGES, @@ -695,6 +696,7 @@ const STEPS = [ { id: 'deps', label: 'Dependency Scanning', icon: '📦', shortLabel: 'Dependencies' }, { id: 'sast', label: 'SAST', icon: '🔎', shortLabel: 'SAST' }, { id: 'dast', label: 'DAST', icon: '⚡', shortLabel: 'DAST' }, + { id: 'predictive', label: 'Predictive Scanning', icon: '🧠', shortLabel: 'Predictive' }, { id: 'scorecard', label: 'Scorecard', icon: '📊', shortLabel: 'Scorecard' }, ] as const; @@ -754,6 +756,7 @@ export default function SecurityDashboard() { {activeStep === 'deps' && } {activeStep === 'sast' && } {activeStep === 'dast' && } + {activeStep === 'predictive' && } {activeStep === 'scorecard' && }
diff --git a/src/utils/monitoring.ts b/src/utils/monitoring.ts index 570c9b6f..c70ff1e1 100644 --- a/src/utils/monitoring.ts +++ b/src/utils/monitoring.ts @@ -25,13 +25,6 @@ import { import { initPerformanceMonitoring } from '../lib/performance'; import { createLogger } from './logger'; -export { - collectHealthSnapshot, - collectSystemHealthSnapshot, - computeHealthScore, - watchErrors, -} from './monitoring.js'; - const logger = createLogger('Monitoring'); // ─── Config ─────────────────────────────────────────────────────────────────── diff --git a/tests/e2e/freighter.spec.js b/tests/e2e/freighter.spec.js index a06883b7..6d8bbc2f 100644 --- a/tests/e2e/freighter.spec.js +++ b/tests/e2e/freighter.spec.js @@ -1,5 +1,9 @@ import { test, expect } from '@playwright/test'; import * as path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); test.describe('Freighter Wallet Flows', () => { const freighterMockPath = path.resolve(__dirname, 'fixtures', 'freighter-mock.js');