diff --git a/.env.example b/.env.example index 5ebc33b..b15d60f 100644 --- a/.env.example +++ b/.env.example @@ -85,6 +85,9 @@ ORACLE_RELAYER_SECRET= # Grant the runtime IAM role access to this secret; never put the signing key # in an environment variable. Use a dedicated, low-value testnet key. ORACLE_RELAYER_SECRET_ID= +# Dedicated Ed25519 seed used to sign oracle decision attestations. Generate +# with `openssl rand -base64 32`; keep it separate from the Stellar relayer key. +ORACLE_ATTESTATION_PRIVATE_KEY= # Multi-key relayer keystore: a JSON file (mode 0600, mounted from a secret # manager) with a "keys" array of { id, secret, network, active } entries. # Re-read when it changes, so rotation/revocation needs no restart. Takes @@ -474,4 +477,3 @@ ORACLE_RELAYER_SECRET_ID= # HORIZON_FINALITY_DEPTH=5 # final_only (default) | allow_provisional # HORIZON_FINALITY_POLICY=final_only - diff --git a/package-lock.json b/package-lock.json index 07fb283..9b55c08 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,6 +48,7 @@ "@types/node": "^20.10.0", "@types/pg": "^8.10.9", "@types/supertest": "^7.2.0", + "@types/uuid": "^9.0.7", "@types/ws": "^8.5.10", "@typescript-eslint/eslint-plugin": "^7.0.0", "@typescript-eslint/parser": "^7.0.0", @@ -56,6 +57,7 @@ "fast-check": "^4.10.2", "jest": "^30.4.2", "nodemon": "^3.0.2", + "source-map-support": "^0.5.21", "supertest": "^7.2.2", "ts-jest": "^29.4.11", "ts-node": "^10.9.2", @@ -183,15 +185,15 @@ } }, "node_modules/@aws-sdk/client-secrets-manager": { - "version": "3.1142.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.1142.0.tgz", - "integrity": "sha512-oYcVufrwTo594OphwaN+FGJRd0dy9N+nialpb6Pt1ykvp03+vDqbA5VYkpDDGNG/0+FBYgZp/1O064R3Vpm+UA==", + "version": "3.1154.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.1154.0.tgz", + "integrity": "sha512-iz4f+mRJiN+SLvdEbkjt2dRSCALYWssBlvXAj3uoCC6cB9+gkUaaV5xYx6CeKACro9fca9QyVzXWB/K9A/wXoQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.1", - "@aws-sdk/credential-provider-node": "^3.972.84", + "@aws-sdk/core": "^3.978.4", + "@aws-sdk/credential-provider-node": "^3.972.87", "@aws-sdk/types": "^3.974.6", - "@smithy/core": "^3.35.0", + "@smithy/core": "^3.36.0", "@smithy/fetch-http-handler": "^5.8.0", "@smithy/node-http-handler": "^4.12.1", "@smithy/types": "^4.19.0", @@ -202,16 +204,16 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.978.1", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.978.1.tgz", - "integrity": "sha512-LbY9aGsEiznDWmUc30Nwv3aIX/+dbwTx8KfS0yOC3NPYMO+O91e6jkT1azf34FwjOndq8/Q+RcVVZz5xnerwdg==", + "version": "3.978.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.978.4.tgz", + "integrity": "sha512-JvGp6y9sPX8FSs2K/EmFMZcUfdkHxqGE45+oxwGC75OIh50j2e4cbx7zcR3BwNs2DSGqJdBja5h6Vy89iIDmRQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.974.6", "@aws-sdk/xml-builder": "^3.972.41", "@aws/lambda-invoke-store": "^0.3.0", - "@smithy/core": "^3.35.0", - "@smithy/signature-v4": "^5.7.3", + "@smithy/core": "^3.36.0", + "@smithy/signature-v4": "^5.7.4", "@smithy/types": "^4.19.0", "bowser": "^2.11.0", "tslib": "^2.6.2" @@ -221,14 +223,14 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.972.72", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.72.tgz", - "integrity": "sha512-xTKO/FWJPozTIXbozVnVGoNBhaGba8TBcx+KyUjRVeOlXE+dUc7GTR1cLvu0uTdIdmemzaFbqqCshXeZA1fZew==", + "version": "3.972.75", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.75.tgz", + "integrity": "sha512-nc8Epg98PcYoKNx0VFlwtmZSCP+tuJ1FLWqpslET/gp4h/WYuULSJqtxppM6ywKKDdzNJPvLD7ceRfmxm3Qdxw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.1", + "@aws-sdk/core": "^3.978.4", "@aws-sdk/types": "^3.974.6", - "@smithy/core": "^3.35.0", + "@smithy/core": "^3.36.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -237,14 +239,14 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.972.74", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.74.tgz", - "integrity": "sha512-u91E/hT8f4d1xy0Jl7VG4nVKJ3lxbrZkoBTeSVoJdWBiSEUMwMS/9+e0H/aJVQV//Lt5wuzP+E69v4aRSsNTmw==", + "version": "3.972.77", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.77.tgz", + "integrity": "sha512-KkYFdIQwuw9IOHfEzK78slxmVWuhcm1w6YgE3V3WVxAeRRj9q5J9jlwzpY/oszWxd4SD96FTFkDdRGBdMFF2Mw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.1", + "@aws-sdk/core": "^3.978.4", "@aws-sdk/types": "^3.974.6", - "@smithy/core": "^3.35.0", + "@smithy/core": "^3.36.0", "@smithy/fetch-http-handler": "^5.8.0", "@smithy/node-http-handler": "^4.12.1", "@smithy/types": "^4.19.0", @@ -255,21 +257,21 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.973.17", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.17.tgz", - "integrity": "sha512-ged4KXdBkvIC81bLvNHHuQKdKak/VXhQTR1NWYTTqW0474nlmsxy9O/vlgTIohDDWH3xpBdtVMZRyjb+DnocDA==", + "version": "3.973.20", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.20.tgz", + "integrity": "sha512-OSl2R/0zLkoeKEIy6oP2tu/bsSdY+DGgro2eOffMD2CcP8POv7rS14FY5Jzc5bjnqB1G9wv/aM1akHC2mdlhZw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.1", - "@aws-sdk/credential-provider-env": "^3.972.72", - "@aws-sdk/credential-provider-http": "^3.972.74", - "@aws-sdk/credential-provider-login": "^3.972.79", - "@aws-sdk/credential-provider-process": "^3.972.72", - "@aws-sdk/credential-provider-sso": "^3.973.16", - "@aws-sdk/credential-provider-web-identity": "^3.972.78", - "@aws-sdk/nested-clients": "^3.997.46", + "@aws-sdk/core": "^3.978.4", + "@aws-sdk/credential-provider-env": "^3.972.75", + "@aws-sdk/credential-provider-http": "^3.972.77", + "@aws-sdk/credential-provider-login": "^3.972.82", + "@aws-sdk/credential-provider-process": "^3.972.75", + "@aws-sdk/credential-provider-sso": "^3.973.19", + "@aws-sdk/credential-provider-web-identity": "^3.972.81", + "@aws-sdk/nested-clients": "^3.997.49", "@aws-sdk/types": "^3.974.6", - "@smithy/core": "^3.35.0", + "@smithy/core": "^3.36.0", "@smithy/credential-provider-imds": "^4.5.2", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" @@ -279,15 +281,15 @@ } }, "node_modules/@aws-sdk/credential-provider-login": { - "version": "3.972.79", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.79.tgz", - "integrity": "sha512-L+Z85anONJd8MaiuraO4wRxATCdEejBZ3K3eymzWI5JPXa9sOS9CkIm72PBKqXKX+Z9p9NGMX5AIMXm0LEflgw==", + "version": "3.972.82", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.82.tgz", + "integrity": "sha512-8jyDPtf1WPxeQ/5xlNpyxAD6P7YKwkYeNk8B2vFEDwkPWDvb7c3tQJG5/jbNWbF2yTMIH9tDTueBkdfovYIAPw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.1", - "@aws-sdk/nested-clients": "^3.997.46", + "@aws-sdk/core": "^3.978.4", + "@aws-sdk/nested-clients": "^3.997.49", "@aws-sdk/types": "^3.974.6", - "@smithy/core": "^3.35.0", + "@smithy/core": "^3.36.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -296,19 +298,19 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.972.84", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.84.tgz", - "integrity": "sha512-oHt854odINVwzwsh+c5x69j0ajm4DbqqqVJ+O1ECsCIZeMDAbzFpXItaqP7UZstJj/ATdTk/KFSH0LaNAgV+kA==", + "version": "3.972.87", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.87.tgz", + "integrity": "sha512-TKj2C0x4IMPSSOAlmeryX8fCPvvPS2pFJz2ikV9IGqyh6Ehsg4leMalCTVfV2rvMTrlGAe7X7nK0HZ3P0KZR9g==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "^3.972.72", - "@aws-sdk/credential-provider-http": "^3.972.74", - "@aws-sdk/credential-provider-ini": "^3.973.17", - "@aws-sdk/credential-provider-process": "^3.972.72", - "@aws-sdk/credential-provider-sso": "^3.973.16", - "@aws-sdk/credential-provider-web-identity": "^3.972.78", + "@aws-sdk/credential-provider-env": "^3.972.75", + "@aws-sdk/credential-provider-http": "^3.972.77", + "@aws-sdk/credential-provider-ini": "^3.973.20", + "@aws-sdk/credential-provider-process": "^3.972.75", + "@aws-sdk/credential-provider-sso": "^3.973.19", + "@aws-sdk/credential-provider-web-identity": "^3.972.81", "@aws-sdk/types": "^3.974.6", - "@smithy/core": "^3.35.0", + "@smithy/core": "^3.36.0", "@smithy/credential-provider-imds": "^4.5.2", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" @@ -318,14 +320,14 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.972.72", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.72.tgz", - "integrity": "sha512-rLIp2xbMjX/k9/od7APpqq1ZgXXnV0pOL1Th3ZsL8Wu0TRtBsDTVS8iPqcfRFcHakFxPvR04OSTv2ka2qOb/2A==", + "version": "3.972.75", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.75.tgz", + "integrity": "sha512-ZNm8T/HJNhpS8MikDVAsym8QyUSUuOUGiCkmI3TTya7G6xhOgfRLd2iK91HhQQyvRM36eo6Xm845hHVyfToA2w==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.1", + "@aws-sdk/core": "^3.978.4", "@aws-sdk/types": "^3.974.6", - "@smithy/core": "^3.35.0", + "@smithy/core": "^3.36.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -334,16 +336,16 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.973.16", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.16.tgz", - "integrity": "sha512-IGihaJfFZYacJJr/odqILCoK7W/mvrZ7cuK7ECn3sAu4vLC6u0V8bS7mCGbdugJ8Aum2tnvqmx0F2MRFp2rn9g==", + "version": "3.973.19", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.19.tgz", + "integrity": "sha512-qHGDAGesyWtRw3BapyFkYSUzVNrC1CWPS6+S2lf/MLMEkWB2gR3Bl6imVHy1nhImXKS+gcuUDzbu39BnsHJc1Q==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.1", - "@aws-sdk/nested-clients": "^3.997.46", - "@aws-sdk/token-providers": "3.1138.0", + "@aws-sdk/core": "^3.978.4", + "@aws-sdk/nested-clients": "^3.997.49", + "@aws-sdk/token-providers": "3.1153.0", "@aws-sdk/types": "^3.974.6", - "@smithy/core": "^3.35.0", + "@smithy/core": "^3.36.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -352,15 +354,15 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.972.78", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.78.tgz", - "integrity": "sha512-/y9WvNtlcPBGLR0qc1a+9J/xtYZfVczvLUOuXaVWylzttH7ewsxwHtjmiJSolNrVSDorIxHGHMU61CbonRkmwA==", + "version": "3.972.81", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.81.tgz", + "integrity": "sha512-nHkgkMIC6NHgqfI6UAQq5cCS4bUkNxkT+r9fXCq/X4IKfX/DHGZ8UM3T4b78OXpWK5OlzEzN4kxwyax0W/MUCA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.1", - "@aws-sdk/nested-clients": "^3.997.46", + "@aws-sdk/core": "^3.978.4", + "@aws-sdk/nested-clients": "^3.997.49", "@aws-sdk/types": "^3.974.6", - "@smithy/core": "^3.35.0", + "@smithy/core": "^3.36.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -369,15 +371,15 @@ } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.997.46", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.46.tgz", - "integrity": "sha512-oRxtBcka/JGHGs9l9p9IVajGoTP8vTPmoAzdHGy4Qcy9P5vPnDf6nhIeM/COQNY9k/OahImTRaLkHftoXvfcmQ==", + "version": "3.997.49", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.49.tgz", + "integrity": "sha512-zjqIqzTYLRKhT7TPJDUreK9vG9PjhmnfLCaTaqArkRT7j+Ogl0tuXB7MpSd8rlRfRT+xTOeyNFWaOfyNcf51hg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.1", - "@aws-sdk/signature-v4-multi-region": "^3.996.47", + "@aws-sdk/core": "^3.978.4", + "@aws-sdk/signature-v4-multi-region": "^3.996.48", "@aws-sdk/types": "^3.974.6", - "@smithy/core": "^3.35.0", + "@smithy/core": "^3.36.0", "@smithy/fetch-http-handler": "^5.8.0", "@smithy/node-http-handler": "^4.12.1", "@smithy/types": "^4.19.0", @@ -388,13 +390,13 @@ } }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.996.47", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.47.tgz", - "integrity": "sha512-Zk08macMvQTHzQJCLJVkOlviVoqwYMrpXv4lmLN7b7sAbiMoOK7Go0NYdR5UeF+MW8LIbRmwrNy9u/5VvX1U5g==", + "version": "3.996.48", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.48.tgz", + "integrity": "sha512-cFDXfEbXaGwbnpvR/DyqfxfjxpjXVRVthQLgkwvcfDS5u3+8G9WSj1X1Ag/pTRg/QOeOno1Tq3ycEdrKsCUcOA==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.974.6", - "@smithy/signature-v4": "^5.7.3", + "@smithy/signature-v4": "^5.7.4", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -403,15 +405,15 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.1138.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1138.0.tgz", - "integrity": "sha512-GpyAr0DD63YOEmYFM6Df+gJuIgC92MMTiBK4FTKfxii5MJ9ge20epR7LyroulscYlG89J+ZB2ivFDPjvfQhzdw==", + "version": "3.1153.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1153.0.tgz", + "integrity": "sha512-6bKJ2OfLboxhzeJ3hBDeB8L6emCYf9DfdrjBAharLrY42J1RV/6jA6G8jvfNghzID8Yb40BdSY7jQMtWx9Ot0Q==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.1", - "@aws-sdk/nested-clients": "^3.997.46", + "@aws-sdk/core": "^3.978.4", + "@aws-sdk/nested-clients": "^3.997.49", "@aws-sdk/types": "^3.974.6", - "@smithy/core": "^3.35.0", + "@smithy/core": "^3.36.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -1148,9 +1150,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.21", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", - "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -1205,9 +1207,9 @@ } }, "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.21", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", - "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -1751,9 +1753,9 @@ } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.15.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.2.tgz", - "integrity": "sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.0.tgz", + "integrity": "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==", "dev": true, "license": "MIT", "dependencies": { @@ -2471,14 +2473,14 @@ } }, "node_modules/@nestjs/platform-express": { - "version": "11.2.6", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.2.6.tgz", - "integrity": "sha512-jPypI/+aueizkYG2Bw2kg8GaNsWE7ekFBSQ/Rwet7wEdBA/+q2FA364UbDabqjlR7C3VSNHmP/JXh+8+jXyDOw==", + "version": "11.1.28", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.28.tgz", + "integrity": "sha512-hU+9Sz4m+onHrR5AmelI59QKmY/Re546bPnygnpqqeQdHDiJpBgjWbL4t6Jr73CBpS60cpyng7WzjgphNB9iwA==", "license": "MIT", "dependencies": { "cors": "2.8.6", "express": "5.2.1", - "multer": "2.4.0", + "multer": "2.2.0", "path-to-regexp": "8.4.2", "tslib": "2.8.1" }, @@ -3066,9 +3068,9 @@ } }, "node_modules/@opentelemetry/api": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", - "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.10.0.tgz", + "integrity": "sha512-lMkyes8iHrG+mk6WH7X70MdCLI2RS0FwNBY1PyQ8UemaijE2m5mv5A0Niy3HtbYTFrgkUEQjFMXmSAk2hQDuPQ==", "license": "Apache-2.0", "engines": { "node": ">=8.0.0" @@ -3461,9 +3463,9 @@ } }, "node_modules/@smithy/core": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.35.0.tgz", - "integrity": "sha512-zRMhfkByhT2snNdr1si24vJitU6Cr9ix2MikUfWmkAgp4jrNP0GcKSP5YvwQ+TlI8AZXER5QOGJn3JsVtSD9/A==", + "version": "3.36.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.36.1.tgz", + "integrity": "sha512-WC5W1Jrsfxu1y6LeqXMie7N14qmcdafij/Haye4feVlyMMXLGpYxBeOX6Ee1iRJbDzGG5LVman8U1rrt9MYFHA==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.19.0", @@ -3968,6 +3970,13 @@ "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", "license": "MIT" }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/validator": { "version": "13.15.10", "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", @@ -5457,9 +5466,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.11.26", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.26.tgz", - "integrity": "sha512-GLQdD3y6UF8iVuMJl5fHgE4jdn/ua7n+toKfLgNlg3BqQtOZjpy68T8Tup8/wGWZCDlm7KMg7tPb4MPn7oN0TQ==", + "version": "2.10.43", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.43.tgz", + "integrity": "sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -5535,9 +5544,9 @@ } }, "node_modules/body-parser": { - "version": "1.20.8", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.8.tgz", - "integrity": "sha512-JNcyFQ64OiijEkPzUBTCe+hyPXUD/3LEldGQ6iF5LR1w00mx9o7xtDWHXBY2iItjdCFGoilOLNQbH943ut7pHA==", + "version": "1.20.6", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz", + "integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==", "license": "MIT", "dependencies": { "bytes": "~3.1.2", @@ -5548,7 +5557,7 @@ "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", - "qs": "~6.16.0", + "qs": "~6.15.1", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" @@ -5592,9 +5601,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", - "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", "dev": true, "license": "MIT", "dependencies": { @@ -5615,9 +5624,9 @@ } }, "node_modules/browserslist": { - "version": "4.29.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.2.tgz", - "integrity": "sha512-/u9r8k8ue4ZhHCw9ovDtltpWdXeXzjhdxCGj6vm1RGLPXb8lu33EMdoCHF5Sx8oYBS+Q/FYYNmAqlHncxR3RmA==", + "version": "4.28.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.6.tgz", + "integrity": "sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==", "dev": true, "funding": [ { @@ -5635,11 +5644,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.11.26", - "caniuse-lite": "^1.0.30001812", - "electron-to-chromium": "^1.5.439", - "node-releases": "^2.0.57", - "update-browserslist-db": "^1.3.3" + "baseline-browser-mapping": "^2.10.42", + "caniuse-lite": "^1.0.30001803", + "electron-to-chromium": "^1.5.389", + "node-releases": "^2.0.51", + "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" @@ -5699,7 +5708,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, "license": "MIT" }, "node_modules/busboy": { @@ -5790,9 +5798,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001813", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001813.tgz", - "integrity": "sha512-zfjJo4rM0+fUomGDBW/xcDjhIwz/210DGvip2MAMDZ8KHcRPnOHmEgHPZP0UHlZoxr8fYKVJOqcORhYQcG4FKQ==", + "version": "1.0.30001805", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001805.tgz", + "integrity": "sha512-52noaS3DubycKSXaU30TwPGIp+POyQSUVa5jBEq3vkRkY0kjyb3LQgvhU6WGyCcyXqVLWO0Cw0Q6BSdD0kUfVA==", "dev": true, "funding": [ { @@ -6160,6 +6168,21 @@ "dev": true, "license": "MIT" }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -6531,9 +6554,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.439", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.439.tgz", - "integrity": "sha512-qu6QIPXhsb+CRcAiTMNjR4A1y/7tCYKkKjr5CZXRVih6qkDf79peZ2BpEU3qoDBNCRrcy3Mra3X9nG5oruuA7Q==", + "version": "1.5.389", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.389.tgz", + "integrity": "sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==", "dev": true, "license": "ISC" }, @@ -6782,9 +6805,9 @@ } }, "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.21", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", - "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -6978,9 +7001,9 @@ } }, "node_modules/express": { - "version": "4.22.3", - "resolved": "https://registry.npmjs.org/express/-/express-4.22.3.tgz", - "integrity": "sha512-Bdcs4+3qlpVlx2NRn6fgX2Ue2/gGRaPeawebgclM0ERSCqDpA+owF1fdPwjJUTAJWMTuAaxjDf+hzb0/4eKvvw==", + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", "license": "MIT", "dependencies": { "accepts": "~1.3.8", @@ -7002,9 +7025,9 @@ "methods": "~1.1.2", "on-finished": "~2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "~0.1.13", + "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", - "qs": "~6.16.0", + "qs": "~6.15.1", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", @@ -7142,9 +7165,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", - "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.3.tgz", + "integrity": "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==", "dev": true, "funding": [ { @@ -7405,9 +7428,9 @@ } }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/brace-expansion": { - "version": "1.1.21", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", - "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -7662,16 +7685,16 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", - "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", + "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" } }, "node_modules/glob/node_modules/minimatch": { @@ -8823,6 +8846,27 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, + "node_modules/jest-runner/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runner/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/jest-runtime": { "version": "30.4.2", "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.4.2.tgz", @@ -9046,9 +9090,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", - "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", "dev": true, "funding": [ { @@ -9548,13 +9592,14 @@ "license": "MIT" }, "node_modules/multer": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/multer/-/multer-2.4.0.tgz", - "integrity": "sha512-7dqa0ZcFfzbefdTuIkzOSMvZWC0J7FLqBOjJUZvDCXShIURWKxAyTT1wHhnE5q19c7jOJf43IYYKjBmZVZmvhg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.2.0.tgz", + "integrity": "sha512-6rdyFg2kLrMh9Jee7/BMPuV9lEAd7lLW2YUpF9/YxR7njyoUwwQ0ZPh3TaIY50Sw6vlyD2HW3wGOkTS4P79xrQ==", "license": "MIT", "dependencies": { "append-field": "^1.0.0", "busboy": "^1.6.0", + "concat-stream": "^2.0.0", "type-is": "^1.6.18" }, "engines": { @@ -9659,9 +9704,9 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.57", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.57.tgz", - "integrity": "sha512-kQK9LGGFiHtrWiNhZtA7Qbw17AQz+dmsEKODRIVTXA9+e5MS/2gZEBhYJt13GrAz5/IOZKddH/0Z3TP/Zgo+yw==", + "version": "2.0.51", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", + "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", "dev": true, "license": "MIT", "engines": { @@ -9708,16 +9753,16 @@ } }, "node_modules/nodemon/node_modules/brace-expansion": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", - "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", + "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" } }, "node_modules/nodemon/node_modules/chokidar": { @@ -10473,9 +10518,9 @@ "license": "MIT" }, "node_modules/qs": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", - "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", "license": "BSD-3-Clause", "dependencies": { "es-define-property": "^1.0.1", @@ -10741,9 +10786,9 @@ } }, "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.21", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", - "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -11185,9 +11230,9 @@ } }, "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "license": "MIT", "dependencies": { @@ -11692,27 +11737,6 @@ "dev": true, "license": "MIT" }, - "node_modules/terser/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/terser/node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -11729,9 +11753,9 @@ } }, "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.21", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", - "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -12236,9 +12260,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.3.tgz", - "integrity": "sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, "funding": [ { diff --git a/package.json b/package.json index a4b67f4..0260f03 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "refract-frontend", "refract-contracts" ], + "main": "dist/main.js", "scripts": { "build": "nest build", "dev": "nodemon --exec ts-node src/main.ts", @@ -86,6 +87,7 @@ "@types/node": "^20.10.0", "@types/pg": "^8.10.9", "@types/supertest": "^7.2.0", + "@types/uuid": "^9.0.7", "@types/ws": "^8.5.10", "@typescript-eslint/eslint-plugin": "^7.0.0", "@typescript-eslint/parser": "^7.0.0", diff --git a/scripts/verify-oracle-attestation.mjs b/scripts/verify-oracle-attestation.mjs new file mode 100644 index 0000000..6c63f99 --- /dev/null +++ b/scripts/verify-oracle-attestation.mjs @@ -0,0 +1,33 @@ +#!/usr/bin/env node +import { createHash, createPublicKey, verify } from "node:crypto"; +import { readFile } from "node:fs/promises"; + +const [tokenPath, publicKeyBase64Url] = process.argv.slice(2); +if (!tokenPath || !publicKeyBase64Url) { + console.error("Usage: node scripts/verify-oracle-attestation.mjs "); + process.exit(2); +} + +const { attestation } = JSON.parse(await readFile(tokenPath, "utf8")); +const [version, encodedPayload, encodedSignature, extra] = String(attestation).split("."); +if (version !== "oa1" || !encodedPayload || !encodedSignature || extra !== undefined) { + throw new Error("Invalid compact oracle attestation format"); +} + +const publicBytes = Buffer.from(publicKeyBase64Url, "base64url"); +if (publicBytes.length !== 32) throw new Error("The Ed25519 public key must decode to 32 bytes"); +const keyId = createHash("sha256").update(publicKeyBase64Url).digest("hex").slice(0, 16); +const payload = JSON.parse(Buffer.from(encodedPayload, "base64url").toString("utf8")); +if (payload.version !== 1 || payload.keyId !== keyId) { + throw new Error("Attestation key ID does not match the supplied verification key"); +} + +const key = createPublicKey({ + key: Buffer.concat([Buffer.from("302a300506032b6570032100", "hex"), publicBytes]), + format: "der", + type: "spki", +}); +if (!verify(null, Buffer.from(encodedPayload), key, Buffer.from(encodedSignature, "base64url"))) { + throw new Error("Oracle attestation signature is invalid"); +} +console.log(JSON.stringify({ verified: true, payload }, null, 2)); diff --git a/src/oracle/oracle-attestation.controller.ts b/src/oracle/oracle-attestation.controller.ts new file mode 100644 index 0000000..7a2775e --- /dev/null +++ b/src/oracle/oracle-attestation.controller.ts @@ -0,0 +1,15 @@ +import { Controller, Get } from "@nestjs/common"; +import { OracleAttestationService } from "./oracle-attestation.service"; + +@Controller("api/v1/oracle") +export class OracleAttestationController { + constructor(private readonly attestations: OracleAttestationService) {} + + @Get("verification-keys") + getVerificationKeys() { + return { + version: 1, + keys: this.attestations.getVerificationKeys(), + }; + } +} diff --git a/src/oracle/oracle-attestation.service.spec.ts b/src/oracle/oracle-attestation.service.spec.ts new file mode 100644 index 0000000..b6e95b3 --- /dev/null +++ b/src/oracle/oracle-attestation.service.spec.ts @@ -0,0 +1,60 @@ +import { createHash, createPublicKey, verify } from "node:crypto"; +import { + decodeOracleAttestation, + OracleAttestationService, +} from "./oracle-attestation.service"; +import { OracleReading } from "./oracle-reading"; + +const reading: OracleReading = { + coverageType: "StablecoinDepeg", + type: "oracle_update", + value: 0.99, + threshold: 0.95, + severity: "low", + message: "USDC price [CoinGecko]", +}; + +describe("OracleAttestationService", () => { + it("signs a compact decision with its evidence root and public verification key", () => { + const service = new OracleAttestationService(Buffer.alloc(32, 9).toString("base64")); + const input = { + adapter: "coingecko", + schemaVersion: 1, + digest: "a".repeat(64), + }; + const { token, evidenceRoot } = service.createAttestation(reading, [input], "2026-10-20T00:00:00.000Z"); + const [prefix, encodedPayload, encodedSignature] = token.split("."); + const payload = decodeOracleAttestation(token); + const publicKey = service.getVerificationKeys()[0]; + const publicBytes = Buffer.from(publicKey.publicKey, "base64url"); + const keyId = createHash("sha256").update(publicKey.publicKey).digest("hex").slice(0, 16); + const verificationKey = createPublicKey({ + key: Buffer.concat([Buffer.from("302a300506032b6570032100", "hex"), publicBytes]), + format: "der", + type: "spki", + }); + + expect(prefix).toBe("oa1"); + expect(payload.feed).toBe("StablecoinDepeg"); + expect(payload.inputs).toEqual([input]); + expect(payload.quorum).toEqual({ required: 1, observed: 1, outcome: "met" }); + expect(payload.evidenceRoot).toBe(evidenceRoot); + expect(payload.timestamp).toBe("2026-10-20T00:00:00.000Z"); + expect(payload.keyId).toBe(keyId); + expect(verify(null, Buffer.from(encodedPayload), verificationKey, Buffer.from(encodedSignature, "base64url"))).toBe(true); + }); + + it("signs fail-safe readings with a not-met quorum and rejects missing signing keys", () => { + const service = new OracleAttestationService(Buffer.alloc(32, 3).toString("base64")); + const result = service.createAttestation(reading, [], "2026-10-20T00:00:00.000Z"); + + expect(decodeOracleAttestation(result.token).quorum).toEqual({ + required: 1, + observed: 0, + outcome: "not_met", + }); + expect(() => new OracleAttestationService().createAttestation(reading, [])).toThrow( + "ORACLE_ATTESTATION_PRIVATE_KEY", + ); + }); +}); diff --git a/src/oracle/oracle-attestation.service.ts b/src/oracle/oracle-attestation.service.ts new file mode 100644 index 0000000..fdd360f --- /dev/null +++ b/src/oracle/oracle-attestation.service.ts @@ -0,0 +1,140 @@ +import { Injectable } from "@nestjs/common"; +import { createHash, createPrivateKey, createPublicKey, sign } from "node:crypto"; +import { OracleReading } from "./oracle-reading"; + +const PRIVATE_KEY_PREFIX = Buffer.from("302e020100300506032b657004220420", "hex"); + +export interface AttestationInput { + adapter: string; + schemaVersion: number; + digest: string; + eligible?: boolean; +} + +export interface OracleDecisionPayload { + version: 1; + keyId: string; + feed: string; + decision: { + value: number; + threshold: number; + severity: OracleReading["severity"]; + }; + inputs: AttestationInput[]; + quorum: { + required: number; + observed: number; + outcome: "met" | "not_met"; + }; + evidenceRoot: string; + timestamp: string; +} + +export interface OracleVerificationKey { + keyId: string; + algorithm: "Ed25519"; + publicKey: string; +} + +function base64Url(value: Buffer): string { + return value.toString("base64url"); +} + +function canonicalJson(value: unknown): string { + if (Array.isArray(value)) { + return `[${value.map(canonicalJson).join(",")}]`; + } + if (value !== null && typeof value === "object") { + const entries = Object.entries(value).sort(([left], [right]) => left.localeCompare(right)); + return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${canonicalJson(item)}`).join(",")}}`; + } + return JSON.stringify(value); +} + +function sha256(value: string): string { + return createHash("sha256").update(value).digest("hex"); +} + +@Injectable() +export class OracleAttestationService { + private readonly privateKey: ReturnType | null; + private readonly verificationKey: OracleVerificationKey | null; + + constructor(seed = process.env.ORACLE_ATTESTATION_PRIVATE_KEY?.trim() ?? "") { + if (!seed) { + this.privateKey = null; + this.verificationKey = null; + return; + } + + const seedBytes = Buffer.from(seed, "base64"); + if (seedBytes.length !== 32 || seedBytes.toString("base64") !== seed) { + throw new Error("ORACLE_ATTESTATION_PRIVATE_KEY must be a base64-encoded 32-byte Ed25519 seed"); + } + + this.privateKey = createPrivateKey({ + key: Buffer.concat([PRIVATE_KEY_PREFIX, seedBytes]), + format: "der", + type: "pkcs8", + }); + const publicDer = createPublicKey(this.privateKey).export({ format: "der", type: "spki" }); + const publicBytes = Buffer.from(publicDer).subarray(-32); + this.verificationKey = { + keyId: sha256(publicBytes.toString("base64url")).slice(0, 16), + algorithm: "Ed25519", + publicKey: base64Url(publicBytes), + }; + } + + getVerificationKeys(): OracleVerificationKey[] { + return this.verificationKey ? [this.verificationKey] : []; + } + + createAttestation( + reading: OracleReading, + inputs: AttestationInput[], + timestamp = new Date().toISOString(), + ): { token: string; evidenceRoot: string } { + if (!this.privateKey || !this.verificationKey) { + throw new Error("Oracle attestations are not configured; set ORACLE_ATTESTATION_PRIVATE_KEY"); + } + + const orderedInputs = [...inputs].sort((left, right) => + `${left.adapter}:${left.digest}`.localeCompare(`${right.adapter}:${right.digest}`), + ); + const evidenceRoot = sha256(canonicalJson(orderedInputs)); + const payload: OracleDecisionPayload = { + version: 1, + keyId: this.verificationKey.keyId, + feed: reading.coverageType, + decision: { + value: reading.value, + threshold: reading.threshold, + severity: reading.severity, + }, + inputs: orderedInputs, + quorum: { + required: 1, + observed: orderedInputs.filter((input) => input.eligible !== false).length, + outcome: orderedInputs.some((input) => input.eligible !== false) ? "met" : "not_met", + }, + evidenceRoot, + timestamp, + }; + const encodedPayload = base64Url(Buffer.from(canonicalJson(payload))); + const signature = base64Url(sign(null, Buffer.from(encodedPayload), this.privateKey)); + return { token: `oa1.${encodedPayload}.${signature}`, evidenceRoot }; + } +} + +export function decodeOracleAttestation(token: string): OracleDecisionPayload { + const [version, encodedPayload, signature, extra] = token.split("."); + if (version !== "oa1" || !encodedPayload || !signature || extra !== undefined) { + throw new Error("Invalid compact oracle attestation format"); + } + const payload = JSON.parse(Buffer.from(encodedPayload, "base64url").toString("utf8")) as OracleDecisionPayload; + if (payload.version !== 1 || payload.keyId.length === 0) { + throw new Error("Unsupported oracle attestation payload"); + } + return payload; +} diff --git a/src/oracle/oracle-reading.ts b/src/oracle/oracle-reading.ts index 242d2d5..70d32fc 100644 --- a/src/oracle/oracle-reading.ts +++ b/src/oracle/oracle-reading.ts @@ -3,6 +3,14 @@ import { FreshnessRejectionReason } from "./freshnessPolicy"; export type Severity = "low" | "medium" | "high" | "triggered"; +export interface OracleEvidenceMetadata { + adapter: string; + schemaVersion: number; + digest: string; + attribution: string; + sensitivity: "public" | "commercial" | "personal" | "synthetic"; +} + export interface OracleReading { readingId?: string; coverageType: string; diff --git a/src/oracle/oracle-source-adapters.spec.ts b/src/oracle/oracle-source-adapters.spec.ts new file mode 100644 index 0000000..2b44ad4 --- /dev/null +++ b/src/oracle/oracle-source-adapters.spec.ts @@ -0,0 +1,65 @@ +import { + captureProviderPayload, + PROVIDER_SCHEMA_DEPRECATION_DAYS, +} from "./oracle-source-adapters"; + +describe("captureProviderPayload", () => { + it("accepts additive provider fields by default and hashes only the normalized payload", () => { + const captured = captureProviderPayload("coingecko", { + "usd-coin": { usd: 0.99, provider_extension: "ignored" }, + request_id: "not-retained", + }); + + expect(captured.payload).toEqual({ "usd-coin": { usd: 0.99 } }); + expect(captured.adapter).toBe("coingecko"); + expect(captured.schemaVersion).toBe(1); + expect(captured.attribution).toBe("CoinGecko"); + expect(captured.policy.rawPayloadStorage).toBe("transient_only"); + expect(captured.digest).toMatch(/^[a-f0-9]{64}$/); + }); + + it("rejects unexpected fields in strict mode, including nested flight identifiers", () => { + expect(() => + captureProviderPayload( + "aviationstack", + { flight: { iata: "BA249", delay: 150, passenger_name: "must-not-be-kept" } }, + { strict: true }, + ), + ).toThrow(); + }); + + it("strips passenger-identifying fields and prohibits retaining flight payloads", () => { + const captured = captureProviderPayload("aviationstack", { + flight: { iata: "BA249", delay: 150, passenger_name: "must-not-be-kept" }, + passenger: { email: "must-not-be-kept@example.test" }, + }); + + expect(captured.payload).toEqual({ flight: { iata: "BA249", delay: 150 } }); + expect(captured.policy).toMatchObject({ + sensitivity: "personal", + rawPayloadStorage: "prohibited", + publicApiPayload: "derived_only", + }); + expect(JSON.stringify(captured)).not.toContain("must-not-be-kept"); + }); + + it("normalizes legacy response envelopes during the 90-day deprecation window only", () => { + const start = Date.parse("2026-10-20T00:00:00.000Z"); + const insideWindow = start + (PROVIDER_SCHEMA_DEPRECATION_DAYS - 1) * 86_400_000; + const captured = captureProviderPayload( + "coingecko", + { data: { "usd-coin": { usd: 1 } } }, + { schemaVersion: 0, now: insideWindow }, + ); + expect(captured.schemaVersion).toBe(0); + expect(captured.payload).toEqual({ "usd-coin": { usd: 1 } }); + + expect(() => + captureProviderPayload( + "coingecko", + { data: { "usd-coin": { usd: 1 } } }, + { schemaVersion: 0, now: start + (PROVIDER_SCHEMA_DEPRECATION_DAYS + 1) * 86_400_000 }, + ), + ).toThrow("passed its deprecation window"); + }); +}); diff --git a/src/oracle/oracle-source-adapters.ts b/src/oracle/oracle-source-adapters.ts new file mode 100644 index 0000000..d5a293d --- /dev/null +++ b/src/oracle/oracle-source-adapters.ts @@ -0,0 +1,142 @@ +import { createHash } from "node:crypto"; +import { z } from "zod"; + +export const PROVIDER_SCHEMA_DEPRECATION_DAYS = 90; +const providerAdapters = ["coingecko", "horizon", "defillama", "aviationstack"] as const; +export type ProviderAdapterId = (typeof providerAdapters)[number]; + +export interface ProviderDataPolicy { + sensitivity: "public" | "commercial" | "personal"; + attribution: string; + rawPayloadStorage: "transient_only" | "prohibited"; + publicApiPayload: "derived_only"; +} + +export interface CapturedProviderEvidence { + adapter: ProviderAdapterId; + schemaVersion: number; + digest: string; + attribution: string; + policy: ProviderDataPolicy; + payload: unknown; +} + +const policies: Record = { + coingecko: { + sensitivity: "commercial", + attribution: "CoinGecko", + rawPayloadStorage: "transient_only", + publicApiPayload: "derived_only", + }, + horizon: { + sensitivity: "public", + attribution: "Stellar Horizon", + rawPayloadStorage: "transient_only", + publicApiPayload: "derived_only", + }, + defillama: { + sensitivity: "commercial", + attribution: "DeFiLlama", + rawPayloadStorage: "transient_only", + publicApiPayload: "derived_only", + }, + aviationstack: { + sensitivity: "personal", + attribution: "AviationStack (when configured)", + rawPayloadStorage: "prohibited", + publicApiPayload: "derived_only", + }, +}; + +const LEGACY_SCHEMA_RELEASED_AT = Date.parse("2026-10-20T00:00:00.000Z"); + +function sha256(value: string): string { + return createHash("sha256").update(value).digest("hex"); +} + +/** + * Provider changes are normalized here before any value can be persisted or + * exposed. Version 0 is the legacy axios `{ data: payload }` envelope. + */ +export function captureProviderPayload( + adapter: ProviderAdapterId, + payload: unknown, + options: { schemaVersion?: number; strict?: boolean; now?: number } = {}, +): CapturedProviderEvidence { + const schemaVersion = options.schemaVersion ?? 1; + const now = options.now ?? Date.now(); + let normalized = payload; + + if (schemaVersion === 0) { + const deprecationDeadline = + LEGACY_SCHEMA_RELEASED_AT + PROVIDER_SCHEMA_DEPRECATION_DAYS * 24 * 60 * 60 * 1000; + if (now > deprecationDeadline) { + throw new Error(`${adapter} schema version 0 has passed its deprecation window`); + } + if (!payload || typeof payload !== "object" || !("data" in payload)) { + throw new Error(`${adapter} schema version 0 requires the legacy data envelope`); + } + normalized = (payload as { data: unknown }).data; + } else if (schemaVersion !== 1) { + throw new Error(`Unsupported ${adapter} schema version ${schemaVersion}`); + } + + const schema = buildSchema(adapter, options.strict ?? false); + const parsed = schema.parse(normalized); + if ( + adapter === "coingecko" && + !(parsed as { "usd-coin"?: unknown; stellar?: unknown })["usd-coin"] && + !(parsed as { stellar?: unknown }).stellar + ) { + throw new Error("CoinGecko payload must contain usd-coin or stellar data"); + } + const attribution = policies[adapter].attribution; + const digest = sha256(JSON.stringify(parsed)); + + return { + adapter, + schemaVersion, + digest, + attribution, + policy: policies[adapter], + payload: parsed, + }; +} + +function buildSchema(adapter: ProviderAdapterId, strict: boolean): z.ZodTypeAny { + const object = (shape: T) => + strict ? z.object(shape).strict() : z.object(shape); + + switch (adapter) { + case "coingecko": + return object({ + "usd-coin": object({ usd: z.number().finite() }).optional(), + stellar: object({ + usd: z.number().finite(), + usd_24h_change: z.number().finite(), + }).optional(), + }); + case "horizon": + return object({ + _embedded: object({ + records: z.array( + object({ sequence: z.number().int(), closed_at: z.string() }), + ), + }), + }); + case "defillama": + return object({ + name: z.string().optional(), + tvl: z.array( + object({ date: z.number().int(), totalLiquidityUSD: z.number().finite() }), + ), + }); + case "aviationstack": + return object({ + flight: object({ + iata: z.string(), + delay: z.number().finite().nullable().optional(), + }), + }); + } +} diff --git a/src/oracle/oracle.gateway.spec.ts b/src/oracle/oracle.gateway.spec.ts index 16d399a..8f1680b 100644 --- a/src/oracle/oracle.gateway.spec.ts +++ b/src/oracle/oracle.gateway.spec.ts @@ -15,7 +15,7 @@ function buildReading(overrides: Partial = {}): OracleReading { } function mockClient(readyState: number, sendImpl?: () => void): WebSocket { - return { readyState, send: jest.fn(sendImpl) } as unknown as WebSocket; + return { readyState, on: jest.fn(), send: jest.fn(sendImpl) } as unknown as WebSocket; } describe("OracleGateway", () => { diff --git a/src/oracle/oracle.gateway.ts b/src/oracle/oracle.gateway.ts index 54c2078..dbfc59b 100644 --- a/src/oracle/oracle.gateway.ts +++ b/src/oracle/oracle.gateway.ts @@ -125,6 +125,31 @@ export class OracleGateway this.logger.log('Oracle WebSocket client disconnected'); } + broadcastAlert(reading: { coverageType: string; message: string; value: number; threshold: number; severity: string }): void { + if (!this.server || !this.server.clients) { + return; + } + + const payload = JSON.stringify({ + type: 'oracle_alert', + ...reading, + }); + + for (const client of this.server.clients) { + if (!client || client.readyState !== WebSocket.OPEN) { + continue; + } + + try { + client.send(payload); + } catch (err) { + this.logger.warn( + `Failed to broadcast Oracle alert to client: ${(err as Error).message}`, + ); + } + } + } + /** Authenticated (ops) clients present `?token=` equal to OPS_WS_TOKEN. */ private isAuthed(req?: IncomingMessage): boolean { const expected = process.env.OPS_WS_TOKEN; diff --git a/src/parameters/protocol-parameter-key.enum.ts b/src/parameters/protocol-parameter-key.enum.ts new file mode 100644 index 0000000..decedcd --- /dev/null +++ b/src/parameters/protocol-parameter-key.enum.ts @@ -0,0 +1,3 @@ +export enum ProtocolParameterKey { + ORACLE_SCHEDULER_INTERVAL_SECONDS = "ORACLE_SCHEDULER_INTERVAL_SECONDS", +} diff --git a/src/parameters/protocol-parameters.service.ts b/src/parameters/protocol-parameters.service.ts new file mode 100644 index 0000000..8aba728 --- /dev/null +++ b/src/parameters/protocol-parameters.service.ts @@ -0,0 +1,13 @@ +import { Injectable } from '@nestjs/common'; +import { ProtocolParameterKey } from './protocol-parameter-key.enum'; + +@Injectable() +export class ProtocolParametersService { + async getNumber(key: ProtocolParameterKey): Promise { + const defaults: Record = { + [ProtocolParameterKey.ORACLE_SCHEDULER_INTERVAL_SECONDS]: 60, + }; + + return defaults[key] ?? 60; + } +}