diff --git a/package-lock.json b/package-lock.json index 36b3816e8f..003f4f5902 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1813,31 +1813,44 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-iam/-/client-iam-3.840.0.tgz", - "integrity": "sha512-+HWqpTwXQYhFzgwfjGFHfo+a0mRQwYq29BEYlgfcydo8UOApc1oxsVmEmnYh2nbukaefUkOaMDb1xORybsE6Lw==", - "dev": true, - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-s3": { + "version": "3.839.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.839.0.tgz", + "integrity": "sha512-7zDInY+qltKxeG+9d/97nbs+FWINcAi5bChBrleUQkuQ/dA9pSP1URo/6JlVzD2Ejvksm+hVK6z3VUWZaIAVOw==", "dependencies": { + "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.840.0", - "@aws-sdk/credential-provider-node": "3.840.0", - "@aws-sdk/middleware-host-header": "3.840.0", - "@aws-sdk/middleware-logger": "3.840.0", - "@aws-sdk/middleware-recursion-detection": "3.840.0", - "@aws-sdk/middleware-user-agent": "3.840.0", - "@aws-sdk/region-config-resolver": "3.840.0", - "@aws-sdk/types": "3.840.0", - "@aws-sdk/util-endpoints": "3.840.0", - "@aws-sdk/util-user-agent-browser": "3.840.0", - "@aws-sdk/util-user-agent-node": "3.840.0", + "@aws-sdk/core": "3.839.0", + "@aws-sdk/credential-provider-node": "3.839.0", + "@aws-sdk/middleware-bucket-endpoint": "3.830.0", + "@aws-sdk/middleware-expect-continue": "3.821.0", + "@aws-sdk/middleware-flexible-checksums": "3.839.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-location-constraint": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-sdk-s3": "3.839.0", + "@aws-sdk/middleware-ssec": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.839.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/signature-v4-multi-region": "3.839.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.828.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.839.0", + "@aws-sdk/xml-builder": "3.821.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.6.0", + "@smithy/eventstream-serde-browser": "^4.0.4", + "@smithy/eventstream-serde-config-resolver": "^4.1.2", + "@smithy/eventstream-serde-node": "^4.0.4", "@smithy/fetch-http-handler": "^5.0.4", + "@smithy/hash-blob-browser": "^4.0.4", "@smithy/hash-node": "^4.0.4", + "@smithy/hash-stream-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", + "@smithy/md5-js": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.13", "@smithy/middleware-retry": "^4.1.14", @@ -1857,33 +1870,34 @@ "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.6", + "@smithy/util-stream": "^4.2.2", "@smithy/util-utf8": "^4.0.0", "@smithy/util-waiter": "^4.0.6", - "tslib": "^2.6.2" + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/client-sso": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.840.0.tgz", - "integrity": "sha512-3Zp+FWN2hhmKdpS0Ragi5V2ZPsZNScE3jlbgoJjzjI/roHZqO+e3/+XFN4TlM0DsPKYJNp+1TAjmhxN6rOnfYA==", - "dev": true, - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sso": { + "version": "3.839.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.839.0.tgz", + "integrity": "sha512-AZABysUhbfcwXVlMo97/vwHgsfJNF81wypCAowpqAJkSjP2KrqsqHpb71/RoR2w8JGmEnBBXRD4wIxDhnmifWg==", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.840.0", - "@aws-sdk/middleware-host-header": "3.840.0", - "@aws-sdk/middleware-logger": "3.840.0", - "@aws-sdk/middleware-recursion-detection": "3.840.0", - "@aws-sdk/middleware-user-agent": "3.840.0", - "@aws-sdk/region-config-resolver": "3.840.0", - "@aws-sdk/types": "3.840.0", - "@aws-sdk/util-endpoints": "3.840.0", - "@aws-sdk/util-user-agent-browser": "3.840.0", - "@aws-sdk/util-user-agent-node": "3.840.0", + "@aws-sdk/core": "3.839.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.839.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.828.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.839.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.6.0", "@smithy/fetch-http-handler": "^5.0.4", @@ -1915,14 +1929,12 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/core": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.840.0.tgz", - "integrity": "sha512-x3Zgb39tF1h2XpU+yA4OAAQlW6LVEfXNlSedSYJ7HGKXqA/E9h3rWQVpYfhXXVVsLdYXdNw5KBUkoAoruoZSZA==", - "dev": true, - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/core": { + "version": "3.839.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.839.0.tgz", + "integrity": "sha512-KdwL5RaK7eUIlOpdOoZ5u+2t4X1rdX/MTZgz3IV/aBzjVUoGsp+uUnbyqXomLQSUitPHp72EE/NHDsvWW/IHvQ==", "dependencies": { - "@aws-sdk/types": "3.840.0", + "@aws-sdk/types": "3.821.0", "@aws-sdk/xml-builder": "3.821.0", "@smithy/core": "^3.6.0", "@smithy/node-config-provider": "^4.1.3", @@ -1942,15 +1954,13 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-env": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.840.0.tgz", - "integrity": "sha512-EzF6VcJK7XvQ/G15AVEfJzN2mNXU8fcVpXo4bRyr1S6t2q5zx6UPH/XjDbn18xyUmOq01t+r8gG+TmHEVo18fA==", - "dev": true, - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.839.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.839.0.tgz", + "integrity": "sha512-cWTadewPPz1OvObZJB+olrgh8VwcgIVcT293ZUT9V0CMF0UU7QaPwJP7uNXcNxltTh+sk1yhjH4UlcnJigZZbA==", "dependencies": { - "@aws-sdk/core": "3.840.0", - "@aws-sdk/types": "3.840.0", + "@aws-sdk/core": "3.839.0", + "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" @@ -1959,15 +1969,13 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-http": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.840.0.tgz", - "integrity": "sha512-wbnUiPGLVea6mXbUh04fu+VJmGkQvmToPeTYdHE8eRZq3NRDi3t3WltT+jArLBKD/4NppRpMjf2ju4coMCz91g==", - "dev": true, - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.839.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.839.0.tgz", + "integrity": "sha512-fv0BZwrDhWDju4D1MCLT4I2aPjr0dVQ6P+MpqvcGNOA41Oa9UdRhYTV5iuy5NLXzIzoCmnS+XfSq5Kbsf6//xw==", "dependencies": { - "@aws-sdk/core": "3.840.0", - "@aws-sdk/types": "3.840.0", + "@aws-sdk/core": "3.839.0", + "@aws-sdk/types": "3.821.0", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/node-http-handler": "^4.0.6", "@smithy/property-provider": "^4.0.4", @@ -1981,21 +1989,19 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.840.0.tgz", - "integrity": "sha512-7F290BsWydShHb+7InXd+IjJc3mlEIm9I0R57F/Pjl1xZB69MdkhVGCnuETWoBt4g53ktJd6NEjzm/iAhFXFmw==", - "dev": true, - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.839.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.839.0.tgz", + "integrity": "sha512-GHm0hF4CiDxIDR7TauMaA6iI55uuSqRxMBcqTAHaTPm6+h1A+MS+ysQMxZ+Jvwtoy8WmfTIGrJVxSCw0sK2hvA==", "dependencies": { - "@aws-sdk/core": "3.840.0", - "@aws-sdk/credential-provider-env": "3.840.0", - "@aws-sdk/credential-provider-http": "3.840.0", - "@aws-sdk/credential-provider-process": "3.840.0", - "@aws-sdk/credential-provider-sso": "3.840.0", - "@aws-sdk/credential-provider-web-identity": "3.840.0", - "@aws-sdk/nested-clients": "3.840.0", - "@aws-sdk/types": "3.840.0", + "@aws-sdk/core": "3.839.0", + "@aws-sdk/credential-provider-env": "3.839.0", + "@aws-sdk/credential-provider-http": "3.839.0", + "@aws-sdk/credential-provider-process": "3.839.0", + "@aws-sdk/credential-provider-sso": "3.839.0", + "@aws-sdk/credential-provider-web-identity": "3.839.0", + "@aws-sdk/nested-clients": "3.839.0", + "@aws-sdk/types": "3.821.0", "@smithy/credential-provider-imds": "^4.0.6", "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", @@ -2006,20 +2012,18 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.840.0.tgz", - "integrity": "sha512-KufP8JnxA31wxklLm63evUPSFApGcH8X86z3mv9SRbpCm5ycgWIGVCTXpTOdgq6rPZrwT9pftzv2/b4mV/9clg==", - "dev": true, - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.839.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.839.0.tgz", + "integrity": "sha512-7bR+U2h+ft0V8chyeu9Bh/pvau4ZkQMeRt5f0dAULoepZQ77QQVRP4H04yJPTg9DCtqbVULQ3uf5YOp1/08vQw==", "dependencies": { - "@aws-sdk/credential-provider-env": "3.840.0", - "@aws-sdk/credential-provider-http": "3.840.0", - "@aws-sdk/credential-provider-ini": "3.840.0", - "@aws-sdk/credential-provider-process": "3.840.0", - "@aws-sdk/credential-provider-sso": "3.840.0", - "@aws-sdk/credential-provider-web-identity": "3.840.0", - "@aws-sdk/types": "3.840.0", + "@aws-sdk/credential-provider-env": "3.839.0", + "@aws-sdk/credential-provider-http": "3.839.0", + "@aws-sdk/credential-provider-ini": "3.839.0", + "@aws-sdk/credential-provider-process": "3.839.0", + "@aws-sdk/credential-provider-sso": "3.839.0", + "@aws-sdk/credential-provider-web-identity": "3.839.0", + "@aws-sdk/types": "3.821.0", "@smithy/credential-provider-imds": "^4.0.6", "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", @@ -2030,15 +2034,13 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-process": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.840.0.tgz", - "integrity": "sha512-HkDQWHy8tCI4A0Ps2NVtuVYMv9cB4y/IuD/TdOsqeRIAT12h8jDb98BwQPNLAImAOwOWzZJ8Cu0xtSpX7CQhMw==", - "dev": true, - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.839.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.839.0.tgz", + "integrity": "sha512-qShpekjociUZ+isyQNa0P7jo+0q3N2+0eJDg8SGyP6K6hHTcGfiqxTDps+IKl6NreCPhZCBzyI9mWkP0xSDR6g==", "dependencies": { - "@aws-sdk/core": "3.840.0", - "@aws-sdk/types": "3.840.0", + "@aws-sdk/core": "3.839.0", + "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", "@smithy/types": "^4.3.1", @@ -2048,17 +2050,15 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.840.0.tgz", - "integrity": "sha512-2qgdtdd6R0Z1y0KL8gzzwFUGmhBHSUx4zy85L2XV1CXhpRNwV71SVWJqLDVV5RVWVf9mg50Pm3AWrUC0xb0pcA==", - "dev": true, - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.839.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.839.0.tgz", + "integrity": "sha512-w10zBLHhU8SBQcdrSPMI02haLoRGZg+gP7mH/Er8VhIXfHefbr7o4NirmB0hwdw/YAH8MLlC9jj7c2SJlsNhYA==", "dependencies": { - "@aws-sdk/client-sso": "3.840.0", - "@aws-sdk/core": "3.840.0", - "@aws-sdk/token-providers": "3.840.0", - "@aws-sdk/types": "3.840.0", + "@aws-sdk/client-sso": "3.839.0", + "@aws-sdk/core": "3.839.0", + "@aws-sdk/token-providers": "3.839.0", + "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", "@smithy/types": "^4.3.1", @@ -2068,16 +2068,14 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.840.0.tgz", - "integrity": "sha512-dpEeVXG8uNZSmVXReE4WP0lwoioX2gstk4RnUgrdUE3YaPq8A+hJiVAyc3h+cjDeIqfbsQbZm9qFetKC2LF9dQ==", - "dev": true, - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.839.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.839.0.tgz", + "integrity": "sha512-EvqTc7J1kgmiuxknpCp1S60hyMQvmKxsI5uXzQtcogl/N55rxiXEqnCLI5q6p33q91PJegrcMCM5Q17Afhm5qA==", "dependencies": { - "@aws-sdk/core": "3.840.0", - "@aws-sdk/nested-clients": "3.840.0", - "@aws-sdk/types": "3.840.0", + "@aws-sdk/core": "3.839.0", + "@aws-sdk/nested-clients": "3.839.0", + "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" @@ -2086,14 +2084,12 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.840.0.tgz", - "integrity": "sha512-ub+hXJAbAje94+Ya6c6eL7sYujoE8D4Bumu1NUI8TXjUhVVn0HzVWQjpRLshdLsUp1AW7XyeJaxyajRaJQ8+Xg==", - "dev": true, - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.821.0.tgz", + "integrity": "sha512-xSMR+sopSeWGx5/4pAGhhfMvGBHioVBbqGvDs6pG64xfNwM5vq5s5v6D04e2i+uSTj4qGa71dLUs5I0UzAK3sw==", "dependencies": { - "@aws-sdk/types": "3.840.0", + "@aws-sdk/types": "3.821.0", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" @@ -2102,14 +2098,12 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-logger": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.840.0.tgz", - "integrity": "sha512-lSV8FvjpdllpGaRspywss4CtXV8M7NNNH+2/j86vMH+YCOZ6fu2T/TyFd/tHwZ92vDfHctWkRbQxg0bagqwovA==", - "dev": true, - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-logger": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.821.0.tgz", + "integrity": "sha512-0cvI0ipf2tGx7fXYEEN5fBeZDz2RnHyb9xftSgUsEq7NBxjV0yTZfLJw6Za5rjE6snC80dRN8+bTNR1tuG89zA==", "dependencies": { - "@aws-sdk/types": "3.840.0", + "@aws-sdk/types": "3.821.0", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, @@ -2117,14 +2111,12 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.840.0.tgz", - "integrity": "sha512-Gu7lGDyfddyhIkj1Z1JtrY5NHb5+x/CRiB87GjaSrKxkDaydtX2CU977JIABtt69l9wLbcGDIQ+W0uJ5xPof7g==", - "dev": true, - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.821.0.tgz", + "integrity": "sha512-efmaifbhBoqKG3bAoEfDdcM8hn1psF+4qa7ykWuYmfmah59JBeqHLfz5W9m9JoTwoKPkFcVLWZxnyZzAnVBOIg==", "dependencies": { - "@aws-sdk/types": "3.840.0", + "@aws-sdk/types": "3.821.0", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" @@ -2133,16 +2125,14 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.840.0.tgz", - "integrity": "sha512-hiiMf7BP5ZkAFAvWRcK67Mw/g55ar7OCrvrynC92hunx/xhMkrgSLM0EXIZ1oTn3uql9kH/qqGF0nqsK6K555A==", - "dev": true, - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.839.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.839.0.tgz", + "integrity": "sha512-2u74uRM1JWq6Sf7+3YpjejPM9YkomGt4kWhrmooIBEq1k5r2GTbkH7pNCxBQwBueXM21jAGVDxxeClpTx+5hig==", "dependencies": { - "@aws-sdk/core": "3.840.0", - "@aws-sdk/types": "3.840.0", - "@aws-sdk/util-endpoints": "3.840.0", + "@aws-sdk/core": "3.839.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.828.0", "@smithy/core": "^3.6.0", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", @@ -2152,25 +2142,23 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/nested-clients": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.840.0.tgz", - "integrity": "sha512-LXYYo9+n4hRqnRSIMXLBb+BLz+cEmjMtTudwK1BF6Bn2RfdDv29KuyeDRrPCS3TwKl7ZKmXUmE9n5UuHAPfBpA==", - "dev": true, - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/nested-clients": { + "version": "3.839.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.839.0.tgz", + "integrity": "sha512-Glic0pg2THYP3aRhJORwJJBe1JLtJoEdWV/MFZNyzCklfMwEzpWtZAyxy+tQyFmMeW50uBAnh2R0jhMMcf257w==", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.840.0", - "@aws-sdk/middleware-host-header": "3.840.0", - "@aws-sdk/middleware-logger": "3.840.0", - "@aws-sdk/middleware-recursion-detection": "3.840.0", - "@aws-sdk/middleware-user-agent": "3.840.0", - "@aws-sdk/region-config-resolver": "3.840.0", - "@aws-sdk/types": "3.840.0", - "@aws-sdk/util-endpoints": "3.840.0", - "@aws-sdk/util-user-agent-browser": "3.840.0", - "@aws-sdk/util-user-agent-node": "3.840.0", + "@aws-sdk/core": "3.839.0", + "@aws-sdk/middleware-host-header": "3.821.0", + "@aws-sdk/middleware-logger": "3.821.0", + "@aws-sdk/middleware-recursion-detection": "3.821.0", + "@aws-sdk/middleware-user-agent": "3.839.0", + "@aws-sdk/region-config-resolver": "3.821.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-endpoints": "3.828.0", + "@aws-sdk/util-user-agent-browser": "3.821.0", + "@aws-sdk/util-user-agent-node": "3.839.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.6.0", "@smithy/fetch-http-handler": "^5.0.4", @@ -2202,14 +2190,12 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/region-config-resolver": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.840.0.tgz", - "integrity": "sha512-Qjnxd/yDv9KpIMWr90ZDPtRj0v75AqGC92Lm9+oHXZ8p1MjG5JE2CW0HL8JRgK9iKzgKBL7pPQRXI8FkvEVfrA==", - "dev": true, - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.821.0.tgz", + "integrity": "sha512-t8og+lRCIIy5nlId0bScNpCkif8sc0LhmtaKsbm0ZPm3sCa/WhCbSZibjbZ28FNjVCV+p0D9RYZx0VDDbtWyjw==", "dependencies": { - "@aws-sdk/types": "3.840.0", + "@aws-sdk/types": "3.821.0", "@smithy/node-config-provider": "^4.1.3", "@smithy/types": "^4.3.1", "@smithy/util-config-provider": "^4.0.0", @@ -2220,77 +2206,24 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/token-providers": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.840.0.tgz", - "integrity": "sha512-6BuTOLTXvmgwjK7ve7aTg9JaWFdM5UoMolLVPMyh3wTv9Ufalh8oklxYHUBIxsKkBGO2WiHXytveuxH6tAgTYg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.840.0", - "@aws-sdk/nested-clients": "3.840.0", - "@aws-sdk/types": "3.840.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/types": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.840.0.tgz", - "integrity": "sha512-xliuHaUFZxEx1NSXeLLZ9Dyu6+EJVQKEoD+yM+zqUo3YDZ7medKJWY6fIOKiPX/N7XbLdBYwajb15Q7IL8KkeA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/util-endpoints": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.840.0.tgz", - "integrity": "sha512-eqE9ROdg/Kk0rj3poutyRCFauPDXIf/WSvCqFiRDDVi6QOnCv/M0g2XW8/jSvkJlOyaXkNCptapIp6BeeFFGYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.840.0", - "@smithy/types": "^4.3.1", - "@smithy/util-endpoints": "^3.0.6", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.840.0.tgz", - "integrity": "sha512-JdyZM3EhhL4PqwFpttZu1afDpPJCCc3eyZOLi+srpX11LsGj6sThf47TYQN75HT1CarZ7cCdQHGzP2uy3/xHfQ==", - "dev": true, - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.821.0.tgz", + "integrity": "sha512-irWZHyM0Jr1xhC+38OuZ7JB6OXMLPZlj48thElpsO1ZSLRkLZx5+I7VV6k3sp2yZ7BYbKz/G2ojSv4wdm7XTLw==", "dependencies": { - "@aws-sdk/types": "3.840.0", + "@aws-sdk/types": "3.821.0", "@smithy/types": "^4.3.1", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.840.0.tgz", - "integrity": "sha512-Fy5JUEDQU1tPm2Yw/YqRYYc27W5+QD/J4mYvQvdWjUGZLB5q3eLFMGD35Uc28ZFoGMufPr4OCxK/bRfWROBRHQ==", - "dev": true, - "license": "Apache-2.0", + "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.839.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.839.0.tgz", + "integrity": "sha512-MuunkIG1bJVMtTH7MbjXOrhHleU5wjHz5eCAUc6vj7M9rwol71nqjj9b8RLnkO5gsJcKc29Qk8iV6xQuzKWNMw==", "dependencies": { - "@aws-sdk/middleware-user-agent": "3.840.0", - "@aws-sdk/types": "3.840.0", + "@aws-sdk/middleware-user-agent": "3.839.0", + "@aws-sdk/types": "3.821.0", "@smithy/node-config-provider": "^4.1.3", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" @@ -2307,12 +2240,10 @@ } } }, - "node_modules/@aws-sdk/client-iam/node_modules/@smithy/abort-controller": { + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/abort-controller": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.4.tgz", "integrity": "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA==", - "dev": true, - "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" @@ -2321,12 +2252,10 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@smithy/node-http-handler": { + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/node-http-handler": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.6.tgz", "integrity": "sha512-NqbmSz7AW2rvw4kXhKGrYTiJVDHnMsFnX4i+/FzcZAfbOBauPYs2ekuECkSbtqaxETLLTu9Rl/ex6+I2BKErPA==", - "dev": true, - "license": "Apache-2.0", "dependencies": { "@smithy/abort-controller": "^4.0.4", "@smithy/protocol-http": "^5.1.2", @@ -2338,12 +2267,10 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-iam/node_modules/@smithy/types": { + "node_modules/@aws-sdk/client-s3/node_modules/@smithy/types": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.1.tgz", "integrity": "sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==", - "dev": true, - "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, @@ -2351,44 +2278,89 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-s3": { + "node_modules/@aws-sdk/client-s3/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.731.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.731.0.tgz", + "integrity": "sha512-O4C/UYGgqMsBg21MMApFdgyh8BX568hQhbdoNFmRVTBoSnCZ3w+H4a1wBPX4Gyl0NX+ab6Xxo9rId8HiyPXJ0A==", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.731.0", + "@aws-sdk/middleware-host-header": "3.731.0", + "@aws-sdk/middleware-logger": "3.731.0", + "@aws-sdk/middleware-recursion-detection": "3.731.0", + "@aws-sdk/middleware-user-agent": "3.731.0", + "@aws-sdk/region-config-resolver": "3.731.0", + "@aws-sdk/types": "3.731.0", + "@aws-sdk/util-endpoints": "3.731.0", + "@aws-sdk/util-user-agent-browser": "3.731.0", + "@aws-sdk/util-user-agent-node": "3.731.0", + "@smithy/config-resolver": "^4.0.0", + "@smithy/core": "^3.0.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/hash-node": "^4.0.0", + "@smithy/invalid-dependency": "^4.0.0", + "@smithy/middleware-content-length": "^4.0.0", + "@smithy/middleware-endpoint": "^4.0.0", + "@smithy/middleware-retry": "^4.0.0", + "@smithy/middleware-serde": "^4.0.0", + "@smithy/middleware-stack": "^4.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/url-parser": "^4.0.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.0", + "@smithy/util-defaults-mode-node": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "@smithy/util-middleware": "^4.0.0", + "@smithy/util-retry": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc": { "version": "3.839.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.839.0.tgz", - "integrity": "sha512-7zDInY+qltKxeG+9d/97nbs+FWINcAi5bChBrleUQkuQ/dA9pSP1URo/6JlVzD2Ejvksm+hVK6z3VUWZaIAVOw==", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.839.0.tgz", + "integrity": "sha512-7QnMApYfQBT441YkxObxt1hZ8TdqZH7h0NdYsvbLdEqGROXBDDT+Wq7ZVfsnKjuVUGQ/t75bIqFn7M8cdyESfA==", "dependencies": { - "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.839.0", "@aws-sdk/credential-provider-node": "3.839.0", - "@aws-sdk/middleware-bucket-endpoint": "3.830.0", - "@aws-sdk/middleware-expect-continue": "3.821.0", - "@aws-sdk/middleware-flexible-checksums": "3.839.0", "@aws-sdk/middleware-host-header": "3.821.0", - "@aws-sdk/middleware-location-constraint": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-sdk-s3": "3.839.0", - "@aws-sdk/middleware-ssec": "3.821.0", "@aws-sdk/middleware-user-agent": "3.839.0", "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/signature-v4-multi-region": "3.839.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.839.0", - "@aws-sdk/xml-builder": "3.821.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.6.0", - "@smithy/eventstream-serde-browser": "^4.0.4", - "@smithy/eventstream-serde-config-resolver": "^4.1.2", - "@smithy/eventstream-serde-node": "^4.0.4", "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/hash-blob-browser": "^4.0.4", "@smithy/hash-node": "^4.0.4", - "@smithy/hash-stream-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", - "@smithy/md5-js": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.13", "@smithy/middleware-retry": "^4.1.14", @@ -2408,18 +2380,14 @@ "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.6", - "@smithy/util-stream": "^4.2.2", "@smithy/util-utf8": "^4.0.0", - "@smithy/util-waiter": "^4.0.6", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" + "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sso": { + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/client-sso": { "version": "3.839.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.839.0.tgz", "integrity": "sha512-AZABysUhbfcwXVlMo97/vwHgsfJNF81wypCAowpqAJkSjP2KrqsqHpb71/RoR2w8JGmEnBBXRD4wIxDhnmifWg==", @@ -2467,7 +2435,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/core": { + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/core": { "version": "3.839.0", "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.839.0.tgz", "integrity": "sha512-KdwL5RaK7eUIlOpdOoZ5u+2t4X1rdX/MTZgz3IV/aBzjVUoGsp+uUnbyqXomLQSUitPHp72EE/NHDsvWW/IHvQ==", @@ -2492,7 +2460,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-env": { + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-env": { "version": "3.839.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.839.0.tgz", "integrity": "sha512-cWTadewPPz1OvObZJB+olrgh8VwcgIVcT293ZUT9V0CMF0UU7QaPwJP7uNXcNxltTh+sk1yhjH4UlcnJigZZbA==", @@ -2507,7 +2475,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-http": { + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-http": { "version": "3.839.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.839.0.tgz", "integrity": "sha512-fv0BZwrDhWDju4D1MCLT4I2aPjr0dVQ6P+MpqvcGNOA41Oa9UdRhYTV5iuy5NLXzIzoCmnS+XfSq5Kbsf6//xw==", @@ -2527,7 +2495,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-ini": { + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-ini": { "version": "3.839.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.839.0.tgz", "integrity": "sha512-GHm0hF4CiDxIDR7TauMaA6iI55uuSqRxMBcqTAHaTPm6+h1A+MS+ysQMxZ+Jvwtoy8WmfTIGrJVxSCw0sK2hvA==", @@ -2550,7 +2518,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-node": { + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-node": { "version": "3.839.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.839.0.tgz", "integrity": "sha512-7bR+U2h+ft0V8chyeu9Bh/pvau4ZkQMeRt5f0dAULoepZQ77QQVRP4H04yJPTg9DCtqbVULQ3uf5YOp1/08vQw==", @@ -2572,7 +2540,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-process": { + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-process": { "version": "3.839.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.839.0.tgz", "integrity": "sha512-qShpekjociUZ+isyQNa0P7jo+0q3N2+0eJDg8SGyP6K6hHTcGfiqxTDps+IKl6NreCPhZCBzyI9mWkP0xSDR6g==", @@ -2588,7 +2556,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-sso": { + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-sso": { "version": "3.839.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.839.0.tgz", "integrity": "sha512-w10zBLHhU8SBQcdrSPMI02haLoRGZg+gP7mH/Er8VhIXfHefbr7o4NirmB0hwdw/YAH8MLlC9jj7c2SJlsNhYA==", @@ -2606,7 +2574,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-web-identity": { + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-web-identity": { "version": "3.839.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.839.0.tgz", "integrity": "sha512-EvqTc7J1kgmiuxknpCp1S60hyMQvmKxsI5uXzQtcogl/N55rxiXEqnCLI5q6p33q91PJegrcMCM5Q17Afhm5qA==", @@ -2622,7 +2590,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-host-header": { + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/middleware-host-header": { "version": "3.821.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.821.0.tgz", "integrity": "sha512-xSMR+sopSeWGx5/4pAGhhfMvGBHioVBbqGvDs6pG64xfNwM5vq5s5v6D04e2i+uSTj4qGa71dLUs5I0UzAK3sw==", @@ -2636,7 +2604,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-logger": { + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/middleware-logger": { "version": "3.821.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.821.0.tgz", "integrity": "sha512-0cvI0ipf2tGx7fXYEEN5fBeZDz2RnHyb9xftSgUsEq7NBxjV0yTZfLJw6Za5rjE6snC80dRN8+bTNR1tuG89zA==", @@ -2649,7 +2617,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-recursion-detection": { + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/middleware-recursion-detection": { "version": "3.821.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.821.0.tgz", "integrity": "sha512-efmaifbhBoqKG3bAoEfDdcM8hn1psF+4qa7ykWuYmfmah59JBeqHLfz5W9m9JoTwoKPkFcVLWZxnyZzAnVBOIg==", @@ -2663,7 +2631,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-user-agent": { + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/middleware-user-agent": { "version": "3.839.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.839.0.tgz", "integrity": "sha512-2u74uRM1JWq6Sf7+3YpjejPM9YkomGt4kWhrmooIBEq1k5r2GTbkH7pNCxBQwBueXM21jAGVDxxeClpTx+5hig==", @@ -2680,7 +2648,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/nested-clients": { + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/nested-clients": { "version": "3.839.0", "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.839.0.tgz", "integrity": "sha512-Glic0pg2THYP3aRhJORwJJBe1JLtJoEdWV/MFZNyzCklfMwEzpWtZAyxy+tQyFmMeW50uBAnh2R0jhMMcf257w==", @@ -2728,7 +2696,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/region-config-resolver": { + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/region-config-resolver": { "version": "3.821.0", "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.821.0.tgz", "integrity": "sha512-t8og+lRCIIy5nlId0bScNpCkif8sc0LhmtaKsbm0ZPm3sCa/WhCbSZibjbZ28FNjVCV+p0D9RYZx0VDDbtWyjw==", @@ -2744,7 +2712,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-browser": { + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/util-user-agent-browser": { "version": "3.821.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.821.0.tgz", "integrity": "sha512-irWZHyM0Jr1xhC+38OuZ7JB6OXMLPZlj48thElpsO1ZSLRkLZx5+I7VV6k3sp2yZ7BYbKz/G2ojSv4wdm7XTLw==", @@ -2755,7 +2723,7 @@ "tslib": "^2.6.2" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-node": { + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/util-user-agent-node": { "version": "3.839.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.839.0.tgz", "integrity": "sha512-MuunkIG1bJVMtTH7MbjXOrhHleU5wjHz5eCAUc6vj7M9rwol71nqjj9b8RLnkO5gsJcKc29Qk8iV6xQuzKWNMw==", @@ -2778,7 +2746,7 @@ } } }, - "node_modules/@aws-sdk/client-s3/node_modules/@smithy/abort-controller": { + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/abort-controller": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.4.tgz", "integrity": "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA==", @@ -2790,7 +2758,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@smithy/node-http-handler": { + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/node-http-handler": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.6.tgz", "integrity": "sha512-NqbmSz7AW2rvw4kXhKGrYTiJVDHnMsFnX4i+/FzcZAfbOBauPYs2ekuECkSbtqaxETLLTu9Rl/ex6+I2BKErPA==", @@ -2805,7 +2773,7 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/@smithy/types": { + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/types": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.1.tgz", "integrity": "sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==", @@ -2816,196 +2784,37 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-s3/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/@aws-sdk/client-sso": { + "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/types": { "version": "3.731.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.731.0.tgz", - "integrity": "sha512-O4C/UYGgqMsBg21MMApFdgyh8BX568hQhbdoNFmRVTBoSnCZ3w+H4a1wBPX4Gyl0NX+ab6Xxo9rId8HiyPXJ0A==", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.731.0.tgz", + "integrity": "sha512-NrdkJg6oOUbXR2r9WvHP408CLyvST8cJfp1/jP9pemtjvjPoh6NukbCtiSFdOOb1eryP02CnqQWItfJC1p2Y/Q==", "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.731.0", - "@aws-sdk/middleware-host-header": "3.731.0", - "@aws-sdk/middleware-logger": "3.731.0", - "@aws-sdk/middleware-recursion-detection": "3.731.0", - "@aws-sdk/middleware-user-agent": "3.731.0", - "@aws-sdk/region-config-resolver": "3.731.0", - "@aws-sdk/types": "3.731.0", - "@aws-sdk/util-endpoints": "3.731.0", - "@aws-sdk/util-user-agent-browser": "3.731.0", - "@aws-sdk/util-user-agent-node": "3.731.0", - "@smithy/config-resolver": "^4.0.0", - "@smithy/core": "^3.0.0", - "@smithy/fetch-http-handler": "^5.0.0", - "@smithy/hash-node": "^4.0.0", - "@smithy/invalid-dependency": "^4.0.0", - "@smithy/middleware-content-length": "^4.0.0", - "@smithy/middleware-endpoint": "^4.0.0", - "@smithy/middleware-retry": "^4.0.0", - "@smithy/middleware-serde": "^4.0.0", - "@smithy/middleware-stack": "^4.0.0", - "@smithy/node-config-provider": "^4.0.0", - "@smithy/node-http-handler": "^4.0.0", - "@smithy/protocol-http": "^5.0.0", - "@smithy/smithy-client": "^4.0.0", "@smithy/types": "^4.0.0", - "@smithy/url-parser": "^4.0.0", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.0", - "@smithy/util-defaults-mode-node": "^4.0.0", - "@smithy/util-endpoints": "^3.0.0", - "@smithy/util-middleware": "^4.0.0", - "@smithy/util-retry": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.839.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.839.0.tgz", - "integrity": "sha512-7QnMApYfQBT441YkxObxt1hZ8TdqZH7h0NdYsvbLdEqGROXBDDT+Wq7ZVfsnKjuVUGQ/t75bIqFn7M8cdyESfA==", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.839.0", - "@aws-sdk/credential-provider-node": "3.839.0", - "@aws-sdk/middleware-host-header": "3.821.0", - "@aws-sdk/middleware-logger": "3.821.0", - "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.839.0", - "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.828.0", - "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.839.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.6.0", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/hash-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.13", - "@smithy/middleware-retry": "^4.1.14", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.5", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.21", - "@smithy/util-defaults-mode-node": "^4.0.21", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.6", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/client-sso": { - "version": "3.839.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.839.0.tgz", - "integrity": "sha512-AZABysUhbfcwXVlMo97/vwHgsfJNF81wypCAowpqAJkSjP2KrqsqHpb71/RoR2w8JGmEnBBXRD4wIxDhnmifWg==", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.839.0", - "@aws-sdk/middleware-host-header": "3.821.0", - "@aws-sdk/middleware-logger": "3.821.0", - "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.839.0", - "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.828.0", - "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.839.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.6.0", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/hash-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.13", - "@smithy/middleware-retry": "^4.1.14", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.5", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.21", - "@smithy/util-defaults-mode-node": "^4.0.21", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.6", - "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/core": { - "version": "3.839.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.839.0.tgz", - "integrity": "sha512-KdwL5RaK7eUIlOpdOoZ5u+2t4X1rdX/MTZgz3IV/aBzjVUoGsp+uUnbyqXomLQSUitPHp72EE/NHDsvWW/IHvQ==", + "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/util-endpoints": { + "version": "3.731.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.731.0.tgz", + "integrity": "sha512-riztxTAfncFS9yQWcBJffGgOgLoKSa63ph+rxWJxKl6BHAmWEvHICj1qDcVmnWfIcvJ5cClclY75l9qKaUH7rQ==", "dependencies": { - "@aws-sdk/types": "3.821.0", - "@aws-sdk/xml-builder": "3.821.0", - "@smithy/core": "^3.6.0", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/property-provider": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/signature-v4": "^5.1.2", - "@smithy/smithy-client": "^4.4.5", - "@smithy/types": "^4.3.1", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-utf8": "^4.0.0", - "fast-xml-parser": "4.4.1", + "@aws-sdk/types": "3.731.0", + "@smithy/types": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-env": { - "version": "3.839.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.839.0.tgz", - "integrity": "sha512-cWTadewPPz1OvObZJB+olrgh8VwcgIVcT293ZUT9V0CMF0UU7QaPwJP7uNXcNxltTh+sk1yhjH4UlcnJigZZbA==", + "node_modules/@aws-sdk/client-sso/node_modules/@smithy/abort-controller": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.4.tgz", + "integrity": "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA==", "dependencies": { - "@aws-sdk/core": "3.839.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, @@ -3013,42 +2822,14 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-http": { - "version": "3.839.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.839.0.tgz", - "integrity": "sha512-fv0BZwrDhWDju4D1MCLT4I2aPjr0dVQ6P+MpqvcGNOA41Oa9UdRhYTV5iuy5NLXzIzoCmnS+XfSq5Kbsf6//xw==", + "node_modules/@aws-sdk/client-sso/node_modules/@smithy/node-http-handler": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.6.tgz", + "integrity": "sha512-NqbmSz7AW2rvw4kXhKGrYTiJVDHnMsFnX4i+/FzcZAfbOBauPYs2ekuECkSbtqaxETLLTu9Rl/ex6+I2BKErPA==", "dependencies": { - "@aws-sdk/core": "3.839.0", - "@aws-sdk/types": "3.821.0", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/property-provider": "^4.0.4", + "@smithy/abort-controller": "^4.0.4", "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.5", - "@smithy/types": "^4.3.1", - "@smithy/util-stream": "^4.2.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.839.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.839.0.tgz", - "integrity": "sha512-GHm0hF4CiDxIDR7TauMaA6iI55uuSqRxMBcqTAHaTPm6+h1A+MS+ysQMxZ+Jvwtoy8WmfTIGrJVxSCw0sK2hvA==", - "dependencies": { - "@aws-sdk/core": "3.839.0", - "@aws-sdk/credential-provider-env": "3.839.0", - "@aws-sdk/credential-provider-http": "3.839.0", - "@aws-sdk/credential-provider-process": "3.839.0", - "@aws-sdk/credential-provider-sso": "3.839.0", - "@aws-sdk/credential-provider-web-identity": "3.839.0", - "@aws-sdk/nested-clients": "3.839.0", - "@aws-sdk/types": "3.821.0", - "@smithy/credential-provider-imds": "^4.0.6", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", + "@smithy/querystring-builder": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, @@ -3056,866 +2837,10 @@ "node": ">=18.0.0" } }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.839.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.839.0.tgz", - "integrity": "sha512-7bR+U2h+ft0V8chyeu9Bh/pvau4ZkQMeRt5f0dAULoepZQ77QQVRP4H04yJPTg9DCtqbVULQ3uf5YOp1/08vQw==", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.839.0", - "@aws-sdk/credential-provider-http": "3.839.0", - "@aws-sdk/credential-provider-ini": "3.839.0", - "@aws-sdk/credential-provider-process": "3.839.0", - "@aws-sdk/credential-provider-sso": "3.839.0", - "@aws-sdk/credential-provider-web-identity": "3.839.0", - "@aws-sdk/types": "3.821.0", - "@smithy/credential-provider-imds": "^4.0.6", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-process": { - "version": "3.839.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.839.0.tgz", - "integrity": "sha512-qShpekjociUZ+isyQNa0P7jo+0q3N2+0eJDg8SGyP6K6hHTcGfiqxTDps+IKl6NreCPhZCBzyI9mWkP0xSDR6g==", - "dependencies": { - "@aws-sdk/core": "3.839.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.839.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.839.0.tgz", - "integrity": "sha512-w10zBLHhU8SBQcdrSPMI02haLoRGZg+gP7mH/Er8VhIXfHefbr7o4NirmB0hwdw/YAH8MLlC9jj7c2SJlsNhYA==", - "dependencies": { - "@aws-sdk/client-sso": "3.839.0", - "@aws-sdk/core": "3.839.0", - "@aws-sdk/token-providers": "3.839.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.839.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.839.0.tgz", - "integrity": "sha512-EvqTc7J1kgmiuxknpCp1S60hyMQvmKxsI5uXzQtcogl/N55rxiXEqnCLI5q6p33q91PJegrcMCM5Q17Afhm5qA==", - "dependencies": { - "@aws-sdk/core": "3.839.0", - "@aws-sdk/nested-clients": "3.839.0", - "@aws-sdk/types": "3.821.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.821.0.tgz", - "integrity": "sha512-xSMR+sopSeWGx5/4pAGhhfMvGBHioVBbqGvDs6pG64xfNwM5vq5s5v6D04e2i+uSTj4qGa71dLUs5I0UzAK3sw==", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/middleware-logger": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.821.0.tgz", - "integrity": "sha512-0cvI0ipf2tGx7fXYEEN5fBeZDz2RnHyb9xftSgUsEq7NBxjV0yTZfLJw6Za5rjE6snC80dRN8+bTNR1tuG89zA==", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.821.0.tgz", - "integrity": "sha512-efmaifbhBoqKG3bAoEfDdcM8hn1psF+4qa7ykWuYmfmah59JBeqHLfz5W9m9JoTwoKPkFcVLWZxnyZzAnVBOIg==", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.839.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.839.0.tgz", - "integrity": "sha512-2u74uRM1JWq6Sf7+3YpjejPM9YkomGt4kWhrmooIBEq1k5r2GTbkH7pNCxBQwBueXM21jAGVDxxeClpTx+5hig==", - "dependencies": { - "@aws-sdk/core": "3.839.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.828.0", - "@smithy/core": "^3.6.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/nested-clients": { - "version": "3.839.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.839.0.tgz", - "integrity": "sha512-Glic0pg2THYP3aRhJORwJJBe1JLtJoEdWV/MFZNyzCklfMwEzpWtZAyxy+tQyFmMeW50uBAnh2R0jhMMcf257w==", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.839.0", - "@aws-sdk/middleware-host-header": "3.821.0", - "@aws-sdk/middleware-logger": "3.821.0", - "@aws-sdk/middleware-recursion-detection": "3.821.0", - "@aws-sdk/middleware-user-agent": "3.839.0", - "@aws-sdk/region-config-resolver": "3.821.0", - "@aws-sdk/types": "3.821.0", - "@aws-sdk/util-endpoints": "3.828.0", - "@aws-sdk/util-user-agent-browser": "3.821.0", - "@aws-sdk/util-user-agent-node": "3.839.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.6.0", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/hash-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.13", - "@smithy/middleware-retry": "^4.1.14", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.5", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.21", - "@smithy/util-defaults-mode-node": "^4.0.21", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.6", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/region-config-resolver": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.821.0.tgz", - "integrity": "sha512-t8og+lRCIIy5nlId0bScNpCkif8sc0LhmtaKsbm0ZPm3sCa/WhCbSZibjbZ28FNjVCV+p0D9RYZx0VDDbtWyjw==", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/types": "^4.3.1", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.821.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.821.0.tgz", - "integrity": "sha512-irWZHyM0Jr1xhC+38OuZ7JB6OXMLPZlj48thElpsO1ZSLRkLZx5+I7VV6k3sp2yZ7BYbKz/G2ojSv4wdm7XTLw==", - "dependencies": { - "@aws-sdk/types": "3.821.0", - "@smithy/types": "^4.3.1", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.839.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.839.0.tgz", - "integrity": "sha512-MuunkIG1bJVMtTH7MbjXOrhHleU5wjHz5eCAUc6vj7M9rwol71nqjj9b8RLnkO5gsJcKc29Qk8iV6xQuzKWNMw==", - "dependencies": { - "@aws-sdk/middleware-user-agent": "3.839.0", - "@aws-sdk/types": "3.821.0", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/abort-controller": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.4.tgz", - "integrity": "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA==", - "dependencies": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/node-http-handler": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.6.tgz", - "integrity": "sha512-NqbmSz7AW2rvw4kXhKGrYTiJVDHnMsFnX4i+/FzcZAfbOBauPYs2ekuECkSbtqaxETLLTu9Rl/ex6+I2BKErPA==", - "dependencies": { - "@smithy/abort-controller": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/querystring-builder": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/types": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.1.tgz", - "integrity": "sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/types": { - "version": "3.731.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.731.0.tgz", - "integrity": "sha512-NrdkJg6oOUbXR2r9WvHP408CLyvST8cJfp1/jP9pemtjvjPoh6NukbCtiSFdOOb1eryP02CnqQWItfJC1p2Y/Q==", - "dependencies": { - "@smithy/types": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/util-endpoints": { - "version": "3.731.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.731.0.tgz", - "integrity": "sha512-riztxTAfncFS9yQWcBJffGgOgLoKSa63ph+rxWJxKl6BHAmWEvHICj1qDcVmnWfIcvJ5cClclY75l9qKaUH7rQ==", - "dependencies": { - "@aws-sdk/types": "3.731.0", - "@smithy/types": "^4.0.0", - "@smithy/util-endpoints": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso/node_modules/@smithy/abort-controller": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.4.tgz", - "integrity": "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA==", - "dependencies": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso/node_modules/@smithy/node-http-handler": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.6.tgz", - "integrity": "sha512-NqbmSz7AW2rvw4kXhKGrYTiJVDHnMsFnX4i+/FzcZAfbOBauPYs2ekuECkSbtqaxETLLTu9Rl/ex6+I2BKErPA==", - "dependencies": { - "@smithy/abort-controller": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/querystring-builder": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso/node_modules/@smithy/types": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.1.tgz", - "integrity": "sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sts": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.840.0.tgz", - "integrity": "sha512-h+mu89Wk81Ne+B624GT/pBM5VjuAZueSeQNixhgtQ1QHi6bZzrpz8+lvMSibKO+kXFyQsTLzkyibbxnhLpWQZA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.840.0", - "@aws-sdk/credential-provider-node": "3.840.0", - "@aws-sdk/middleware-host-header": "3.840.0", - "@aws-sdk/middleware-logger": "3.840.0", - "@aws-sdk/middleware-recursion-detection": "3.840.0", - "@aws-sdk/middleware-user-agent": "3.840.0", - "@aws-sdk/region-config-resolver": "3.840.0", - "@aws-sdk/types": "3.840.0", - "@aws-sdk/util-endpoints": "3.840.0", - "@aws-sdk/util-user-agent-browser": "3.840.0", - "@aws-sdk/util-user-agent-node": "3.840.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.6.0", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/hash-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.13", - "@smithy/middleware-retry": "^4.1.14", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.5", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.21", - "@smithy/util-defaults-mode-node": "^4.0.21", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.6", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/client-sso": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.840.0.tgz", - "integrity": "sha512-3Zp+FWN2hhmKdpS0Ragi5V2ZPsZNScE3jlbgoJjzjI/roHZqO+e3/+XFN4TlM0DsPKYJNp+1TAjmhxN6rOnfYA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.840.0", - "@aws-sdk/middleware-host-header": "3.840.0", - "@aws-sdk/middleware-logger": "3.840.0", - "@aws-sdk/middleware-recursion-detection": "3.840.0", - "@aws-sdk/middleware-user-agent": "3.840.0", - "@aws-sdk/region-config-resolver": "3.840.0", - "@aws-sdk/types": "3.840.0", - "@aws-sdk/util-endpoints": "3.840.0", - "@aws-sdk/util-user-agent-browser": "3.840.0", - "@aws-sdk/util-user-agent-node": "3.840.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.6.0", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/hash-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.13", - "@smithy/middleware-retry": "^4.1.14", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.5", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.21", - "@smithy/util-defaults-mode-node": "^4.0.21", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.6", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/core": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.840.0.tgz", - "integrity": "sha512-x3Zgb39tF1h2XpU+yA4OAAQlW6LVEfXNlSedSYJ7HGKXqA/E9h3rWQVpYfhXXVVsLdYXdNw5KBUkoAoruoZSZA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.840.0", - "@aws-sdk/xml-builder": "3.821.0", - "@smithy/core": "^3.6.0", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/property-provider": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/signature-v4": "^5.1.2", - "@smithy/smithy-client": "^4.4.5", - "@smithy/types": "^4.3.1", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-utf8": "^4.0.0", - "fast-xml-parser": "4.4.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-env": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.840.0.tgz", - "integrity": "sha512-EzF6VcJK7XvQ/G15AVEfJzN2mNXU8fcVpXo4bRyr1S6t2q5zx6UPH/XjDbn18xyUmOq01t+r8gG+TmHEVo18fA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.840.0", - "@aws-sdk/types": "3.840.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-http": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.840.0.tgz", - "integrity": "sha512-wbnUiPGLVea6mXbUh04fu+VJmGkQvmToPeTYdHE8eRZq3NRDi3t3WltT+jArLBKD/4NppRpMjf2ju4coMCz91g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.840.0", - "@aws-sdk/types": "3.840.0", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/property-provider": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.5", - "@smithy/types": "^4.3.1", - "@smithy/util-stream": "^4.2.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.840.0.tgz", - "integrity": "sha512-7F290BsWydShHb+7InXd+IjJc3mlEIm9I0R57F/Pjl1xZB69MdkhVGCnuETWoBt4g53ktJd6NEjzm/iAhFXFmw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.840.0", - "@aws-sdk/credential-provider-env": "3.840.0", - "@aws-sdk/credential-provider-http": "3.840.0", - "@aws-sdk/credential-provider-process": "3.840.0", - "@aws-sdk/credential-provider-sso": "3.840.0", - "@aws-sdk/credential-provider-web-identity": "3.840.0", - "@aws-sdk/nested-clients": "3.840.0", - "@aws-sdk/types": "3.840.0", - "@smithy/credential-provider-imds": "^4.0.6", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.840.0.tgz", - "integrity": "sha512-KufP8JnxA31wxklLm63evUPSFApGcH8X86z3mv9SRbpCm5ycgWIGVCTXpTOdgq6rPZrwT9pftzv2/b4mV/9clg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.840.0", - "@aws-sdk/credential-provider-http": "3.840.0", - "@aws-sdk/credential-provider-ini": "3.840.0", - "@aws-sdk/credential-provider-process": "3.840.0", - "@aws-sdk/credential-provider-sso": "3.840.0", - "@aws-sdk/credential-provider-web-identity": "3.840.0", - "@aws-sdk/types": "3.840.0", - "@smithy/credential-provider-imds": "^4.0.6", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-process": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.840.0.tgz", - "integrity": "sha512-HkDQWHy8tCI4A0Ps2NVtuVYMv9cB4y/IuD/TdOsqeRIAT12h8jDb98BwQPNLAImAOwOWzZJ8Cu0xtSpX7CQhMw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.840.0", - "@aws-sdk/types": "3.840.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.840.0.tgz", - "integrity": "sha512-2qgdtdd6R0Z1y0KL8gzzwFUGmhBHSUx4zy85L2XV1CXhpRNwV71SVWJqLDVV5RVWVf9mg50Pm3AWrUC0xb0pcA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-sso": "3.840.0", - "@aws-sdk/core": "3.840.0", - "@aws-sdk/token-providers": "3.840.0", - "@aws-sdk/types": "3.840.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.840.0.tgz", - "integrity": "sha512-dpEeVXG8uNZSmVXReE4WP0lwoioX2gstk4RnUgrdUE3YaPq8A+hJiVAyc3h+cjDeIqfbsQbZm9qFetKC2LF9dQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.840.0", - "@aws-sdk/nested-clients": "3.840.0", - "@aws-sdk/types": "3.840.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.840.0.tgz", - "integrity": "sha512-ub+hXJAbAje94+Ya6c6eL7sYujoE8D4Bumu1NUI8TXjUhVVn0HzVWQjpRLshdLsUp1AW7XyeJaxyajRaJQ8+Xg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.840.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/middleware-logger": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.840.0.tgz", - "integrity": "sha512-lSV8FvjpdllpGaRspywss4CtXV8M7NNNH+2/j86vMH+YCOZ6fu2T/TyFd/tHwZ92vDfHctWkRbQxg0bagqwovA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.840.0", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.840.0.tgz", - "integrity": "sha512-Gu7lGDyfddyhIkj1Z1JtrY5NHb5+x/CRiB87GjaSrKxkDaydtX2CU977JIABtt69l9wLbcGDIQ+W0uJ5xPof7g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.840.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.840.0.tgz", - "integrity": "sha512-hiiMf7BP5ZkAFAvWRcK67Mw/g55ar7OCrvrynC92hunx/xhMkrgSLM0EXIZ1oTn3uql9kH/qqGF0nqsK6K555A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.840.0", - "@aws-sdk/types": "3.840.0", - "@aws-sdk/util-endpoints": "3.840.0", - "@smithy/core": "^3.6.0", - "@smithy/protocol-http": "^5.1.2", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/nested-clients": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.840.0.tgz", - "integrity": "sha512-LXYYo9+n4hRqnRSIMXLBb+BLz+cEmjMtTudwK1BF6Bn2RfdDv29KuyeDRrPCS3TwKl7ZKmXUmE9n5UuHAPfBpA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.840.0", - "@aws-sdk/middleware-host-header": "3.840.0", - "@aws-sdk/middleware-logger": "3.840.0", - "@aws-sdk/middleware-recursion-detection": "3.840.0", - "@aws-sdk/middleware-user-agent": "3.840.0", - "@aws-sdk/region-config-resolver": "3.840.0", - "@aws-sdk/types": "3.840.0", - "@aws-sdk/util-endpoints": "3.840.0", - "@aws-sdk/util-user-agent-browser": "3.840.0", - "@aws-sdk/util-user-agent-node": "3.840.0", - "@smithy/config-resolver": "^4.1.4", - "@smithy/core": "^3.6.0", - "@smithy/fetch-http-handler": "^5.0.4", - "@smithy/hash-node": "^4.0.4", - "@smithy/invalid-dependency": "^4.0.4", - "@smithy/middleware-content-length": "^4.0.4", - "@smithy/middleware-endpoint": "^4.1.13", - "@smithy/middleware-retry": "^4.1.14", - "@smithy/middleware-serde": "^4.0.8", - "@smithy/middleware-stack": "^4.0.4", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/node-http-handler": "^4.0.6", - "@smithy/protocol-http": "^5.1.2", - "@smithy/smithy-client": "^4.4.5", - "@smithy/types": "^4.3.1", - "@smithy/url-parser": "^4.0.4", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.21", - "@smithy/util-defaults-mode-node": "^4.0.21", - "@smithy/util-endpoints": "^3.0.6", - "@smithy/util-middleware": "^4.0.4", - "@smithy/util-retry": "^4.0.6", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/region-config-resolver": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.840.0.tgz", - "integrity": "sha512-Qjnxd/yDv9KpIMWr90ZDPtRj0v75AqGC92Lm9+oHXZ8p1MjG5JE2CW0HL8JRgK9iKzgKBL7pPQRXI8FkvEVfrA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.840.0", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/types": "^4.3.1", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.4", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/token-providers": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.840.0.tgz", - "integrity": "sha512-6BuTOLTXvmgwjK7ve7aTg9JaWFdM5UoMolLVPMyh3wTv9Ufalh8oklxYHUBIxsKkBGO2WiHXytveuxH6tAgTYg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.840.0", - "@aws-sdk/nested-clients": "3.840.0", - "@aws-sdk/types": "3.840.0", - "@smithy/property-provider": "^4.0.4", - "@smithy/shared-ini-file-loader": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/types": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.840.0.tgz", - "integrity": "sha512-xliuHaUFZxEx1NSXeLLZ9Dyu6+EJVQKEoD+yM+zqUo3YDZ7medKJWY6fIOKiPX/N7XbLdBYwajb15Q7IL8KkeA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/util-endpoints": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.840.0.tgz", - "integrity": "sha512-eqE9ROdg/Kk0rj3poutyRCFauPDXIf/WSvCqFiRDDVi6QOnCv/M0g2XW8/jSvkJlOyaXkNCptapIp6BeeFFGYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.840.0", - "@smithy/types": "^4.3.1", - "@smithy/util-endpoints": "^3.0.6", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.840.0.tgz", - "integrity": "sha512-JdyZM3EhhL4PqwFpttZu1afDpPJCCc3eyZOLi+srpX11LsGj6sThf47TYQN75HT1CarZ7cCdQHGzP2uy3/xHfQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.840.0", - "@smithy/types": "^4.3.1", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.840.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.840.0.tgz", - "integrity": "sha512-Fy5JUEDQU1tPm2Yw/YqRYYc27W5+QD/J4mYvQvdWjUGZLB5q3eLFMGD35Uc28ZFoGMufPr4OCxK/bRfWROBRHQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/middleware-user-agent": "3.840.0", - "@aws-sdk/types": "3.840.0", - "@smithy/node-config-provider": "^4.1.3", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/abort-controller": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.4.tgz", - "integrity": "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/node-http-handler": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.6.tgz", - "integrity": "sha512-NqbmSz7AW2rvw4kXhKGrYTiJVDHnMsFnX4i+/FzcZAfbOBauPYs2ekuECkSbtqaxETLLTu9Rl/ex6+I2BKErPA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^4.0.4", - "@smithy/protocol-http": "^5.1.2", - "@smithy/querystring-builder": "^4.0.4", - "@smithy/types": "^4.3.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/types": { + "node_modules/@aws-sdk/client-sso/node_modules/@smithy/types": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.1.tgz", "integrity": "sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==", - "dev": true, - "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, diff --git a/server/aws-lsp-identity/src/iam/iamProvider.test.ts b/server/aws-lsp-identity/src/iam/iamProvider.test.ts new file mode 100644 index 0000000000..66415caf4e --- /dev/null +++ b/server/aws-lsp-identity/src/iam/iamProvider.test.ts @@ -0,0 +1,297 @@ +import { expect, use } from 'chai' +import { StubbedInstance, stubInterface } from 'ts-sinon' +import { ProfileData, ProfileStore } from '../language-server/profiles/profileService' +import { createStubInstance, restore, SinonStub, stub } from 'sinon' +import { CancellationToken, Profile, ProfileKind } from '@aws/language-server-runtimes/protocol' +import { Logging, Telemetry } from '@aws/language-server-runtimes/server-interface' +import { IamCredentials, Observability } from '@aws/lsp-core' +import { StsCache } from '../sts/cache/stsCache' +import { StsAutoRefresher } from '../sts/stsAutoRefresher' +import { IamProvider } from '../iam/iamProvider' +import { IamFlowParams } from './utils' +import * as iamUtils from '../iam/utils' +import { STSClient } from '@aws-sdk/client-sts' + +// eslint-disable-next-line +use(require('chai-as-promised')) + +let sut: IamProvider +let defaultParams: IamFlowParams +let defaultProfile: Profile +let profileStore: StubbedInstance +let stsCache: StubbedInstance +let stsAutoRefresher: StubbedInstance +let handlers: StubbedInstance +let observability: StubbedInstance +let token: StubbedInstance +let checkMfaRequiredStub: SinonStub< + [credentials: IamCredentials, permissions: string[], region?: string | undefined], + Promise +> + +describe('IamProvider', () => { + beforeEach(() => { + defaultProfile = { + kinds: [ProfileKind.Unknown], + name: 'default-profile', + } + + profileStore = stubInterface({ + load: Promise.resolve({ + profiles: [ + { + kinds: [ProfileKind.IamSourceProfileProfile], + name: 'cyclic-profile-1', + settings: { + role_arn: 'my-role-arn', + source_profile: 'cyclic-profile-1', + }, + }, + { + kinds: [ProfileKind.IamSourceProfileProfile], + name: 'cyclic-profile-2', + settings: { + role_arn: 'my-role-arn', + source_profile: 'cyclic-profile-3', + }, + }, + { + kinds: [ProfileKind.IamSourceProfileProfile], + name: 'cyclic-profile-3', + settings: { + role_arn: 'my-role-arn', + source_profile: 'cyclic-profile-2', + }, + }, + { + kinds: [ProfileKind.IamSourceProfileProfile], + name: 'base-profile', + settings: { + role_arn: 'my-role-arn', + source_profile: 'intermediate-profile', + }, + }, + { + kinds: [ProfileKind.IamSourceProfileProfile], + name: 'intermediate-profile', + settings: { + role_arn: 'my-role-arn', + source_profile: 'my-iam-profile', + }, + }, + { + kinds: [ProfileKind.IamCredentialsProfile], + name: 'my-iam-profile', + settings: { + aws_access_key_id: 'my-access-key', + aws_secret_access_key: 'my-secret-key', + }, + }, + ], + ssoSessions: [], + } satisfies ProfileData), + }) + + stsCache = stubInterface({ + getStsCredential: Promise.resolve(undefined), + setStsCredential: Promise.resolve(), + removeStsCredential: Promise.resolve(), + }) + + stsAutoRefresher = createStubInstance(StsAutoRefresher, { + watch: Promise.resolve(), + unwatch: undefined, + }) as StubbedInstance + + observability = stubInterface() + observability.logging = stubInterface() + observability.telemetry = stubInterface() + + handlers = stubInterface({ + sendGetMfaCode: Promise.resolve({ code: 'mfa-code', mfaSerial: 'mfa-serial' }), + }) + + token = stubInterface() + + defaultParams = { + profile: defaultProfile, + callStsOnInvalidIamCredential: true, + recursionCount: 0, + profileStore: profileStore, + stsCache: stsCache, + stsAutoRefresher: stsAutoRefresher, + handlers: handlers, + token: token, + observability: observability, + } + + sut = new IamProvider() + + checkMfaRequiredStub = stub(iamUtils, 'checkMfaRequired') + checkMfaRequiredStub.resolves(false) + + stub(STSClient.prototype, 'send').resolves({ + Credentials: { + AccessKeyId: 'role-access-key', + SecretAccessKey: 'role-secret-key', + SessionToken: 'role-session-token', + Expiration: new Date('2024-09-25T18:09:20.455Z'), + }, + AssumedRoleUser: { + Arn: 'role-arn', + AssumedRoleId: 'role-id', + }, + }) + }) + + afterEach(() => { + restore() + }) + + describe('getCredential', () => { + it('Can get credentials from profile', async () => { + const profile: Profile = { + kinds: [ProfileKind.IamCredentialsProfile], + name: 'iam-profile', + settings: { + aws_access_key_id: 'access-key', + aws_secret_access_key: 'secret-key', + aws_session_token: 'session-token', + }, + } + const actual = await sut.getCredential({ ...defaultParams, profile: profile }) + + expect(actual.credentials.accessKeyId).to.equal('access-key') + expect(actual.credentials.secretAccessKey).to.equal('secret-key') + expect(actual.credentials.sessionToken).to.equal('session-token') + }) + + it('Can generate credentials by assuming role.', async () => { + const profile: Profile = { + kinds: [ProfileKind.IamSourceProfileProfile], + name: 'my-role-profile', + settings: { + role_arn: 'my-role-arn', + source_profile: 'my-iam-profile', + }, + } + const actual = await sut.getCredential({ ...defaultParams, profile: profile }) + + expect(actual.credentials.accessKeyId).to.equal('role-access-key') + expect(actual.credentials.secretAccessKey).to.equal('role-secret-key') + expect(actual.credentials.sessionToken).to.equal('role-session-token') + expect(actual.credentials.expiration?.toISOString()).to.equal('2024-09-25T18:09:20.455Z') + expect(stsAutoRefresher.watch.calledOnce).to.be.true + }) + + it('Can generate credentials with MFA.', async () => { + checkMfaRequiredStub.resolves(true) + const profile: Profile = { + kinds: [ProfileKind.IamSourceProfileProfile], + name: 'my-mfa-profile', + settings: { + role_arn: 'my-role-arn', + source_profile: 'my-iam-profile', + mfa_serial: 'my-device-arn', + }, + } + const actual = await sut.getCredential({ ...defaultParams, profile: profile }) + + expect(actual.credentials.accessKeyId).to.equal('role-access-key') + expect(actual.credentials.secretAccessKey).to.equal('role-secret-key') + expect(actual.credentials.sessionToken).to.equal('role-session-token') + expect(actual.credentials.expiration?.toISOString()).to.equal('2024-09-25T18:09:20.455Z') + expect(handlers.sendGetMfaCode.calledOnce).to.be.true + }) + + it('Returns existing STS credential.', async () => { + const profile: Profile = { + kinds: [ProfileKind.IamSourceProfileProfile], + name: 'my-role-profile', + settings: { + role_arn: 'my-role-arn', + source_profile: 'my-iam-profile', + }, + } + stsCache.getStsCredential = (() => + Promise.resolve({ + accessKeyId: 'other-access-key', + secretAccessKey: 'other-secret-key', + sessionToken: 'other-session-token', + expiration: new Date('2024-10-25T18:09:20.455Z'), + })) as any + const actual = await sut.getCredential({ ...defaultParams, profile: profile }) + + expect(actual.credentials.accessKeyId).to.equal('other-access-key') + expect(actual.credentials.secretAccessKey).to.equal('other-secret-key') + expect(actual.credentials.sessionToken).to.equal('other-session-token') + expect(actual.credentials.expiration?.toISOString()).to.equal('2024-10-25T18:09:20.455Z') + expect(stsAutoRefresher.watch.calledOnce).to.be.true + }) + + it('Throws when no STS credential cached and callStsOnInvalidIamCredential is false.', async () => { + const profile: Profile = { + kinds: [ProfileKind.IamSourceProfileProfile], + name: 'my-role-profile', + settings: { + role_arn: 'my-role-arn', + source_profile: 'my-iam-profile', + }, + } + const error = await expect( + sut.getCredential({ ...defaultParams, profile: profile, callStsOnInvalidIamCredential: false }) + ).rejectedWith(Error) + + expect(error.message).to.equal('STS credential not found.') + expect(stsAutoRefresher.watch.calledOnce).to.be.false + }) + + it('Can login with chained IamSourceProfileProfiles.', async () => { + const profile: Profile = { + kinds: [ProfileKind.IamSourceProfileProfile], + name: 'base-profile', + settings: { + role_arn: 'my-role-arn', + source_profile: 'intermediate-profile', + }, + } + const actual = await sut.getCredential({ ...defaultParams, profile: profile }) + + expect(actual.credentials.accessKeyId).to.equal('role-access-key') + expect(actual.credentials.secretAccessKey).to.equal('role-secret-key') + expect(actual.credentials.sessionToken).to.equal('role-session-token') + expect(actual.credentials.expiration?.toISOString()).to.equal('2024-09-25T18:09:20.455Z') + expect(stsAutoRefresher.watch.called).to.be.true + }) + + it('Throws when IamSourceProfileProfile points to itself.', async () => { + const profile: Profile = { + kinds: [ProfileKind.IamSourceProfileProfile], + name: 'cyclic-profile-1', + settings: { + role_arn: 'my-role-arn', + source_profile: 'cyclic-profile-1', + }, + } + const error = await expect(sut.getCredential({ ...defaultParams, profile: profile })).rejectedWith(Error) + + expect(error.message).to.equal('Source profile chain exceeded max length.') + expect(stsAutoRefresher.watch.calledOnce).to.be.false + }) + + it('Throws when IamSourceProfileProfile form cycle.', async () => { + const profile: Profile = { + kinds: [ProfileKind.IamSourceProfileProfile], + name: 'cyclic-profile-2', + settings: { + role_arn: 'my-role-arn', + source_profile: 'cyclic-profile-3', + }, + } + const error = await expect(sut.getCredential({ ...defaultParams, profile: profile })).rejectedWith(Error) + + expect(error.message).to.equal('Source profile chain exceeded max length.') + expect(stsAutoRefresher.watch.calledOnce).to.be.false + }) + }) +}) diff --git a/server/aws-lsp-identity/src/iam/iamProvider.ts b/server/aws-lsp-identity/src/iam/iamProvider.ts index 42c3cdc7d5..dd93fc118b 100644 --- a/server/aws-lsp-identity/src/iam/iamProvider.ts +++ b/server/aws-lsp-identity/src/iam/iamProvider.ts @@ -1,22 +1,35 @@ -import { AwsErrorCodes, IamCredentials, Profile, ProfileKind } from '@aws/language-server-runtimes/server-interface' -import { AwsError, Observability } from '@aws/lsp-core' -import { ProfileStore } from '../language-server/profiles/profileService' +import { + AwsErrorCodes, + GetMfaCodeResult, + IamCredential, + IamCredentials, + ProfileKind, +} from '@aws/language-server-runtimes/server-interface' +import { AwsError } from '@aws/lsp-core' +import { AssumeRoleCommand, AssumeRoleCommandInput, STSClient } from '@aws-sdk/client-sts' +import { checkMfaRequired, IamFlowParams } from './utils' +import { convertProfileToId } from '../sts/cache/fileSystemStsCache' + +const sourceProfileRecursionMax = 5 +const mfaTimeout = 2 * 60 * 1000 // 2 minutes export class IamProvider { - constructor( - private readonly observability: Observability, // In case we need telemetry and logging in the future - private readonly profileStore: ProfileStore // Will be used when assuming role with source_profile - ) {} + readonly defaultRegion = 'us-east-1' - async getCredential(profile: Profile, callStsOnInvalidIamCredential: boolean): Promise { + async getCredential(params: IamFlowParams): Promise { let credentials: IamCredentials + // Get the credentials directly from the profile - if (profile.kinds.includes(ProfileKind.IamCredentialsProfile)) { + if (params.profile.kinds.includes(ProfileKind.IamCredentialsProfile)) { credentials = { - accessKeyId: profile.settings!.aws_access_key_id!, - secretAccessKey: profile.settings!.aws_secret_access_key!, - sessionToken: profile.settings!.aws_session_token!, + accessKeyId: params.profile.settings!.aws_access_key_id!, + secretAccessKey: params.profile.settings!.aws_secret_access_key!, + sessionToken: params.profile.settings?.aws_session_token, } + } + // Assume the role matching the found ARN + else if (params.profile.kinds.includes(ProfileKind.IamSourceProfileProfile)) { + credentials = await this.getAssumedRoleCredential(params) } else { throw new AwsError( 'Credentials could not be found for provided profile kind', @@ -24,6 +37,151 @@ export class IamProvider { ) } - return credentials + return { id: convertProfileToId(params.profile), kinds: params.profile.kinds, credentials: credentials } + } + + private async getAssumedRoleCredential(params: IamFlowParams): Promise { + if (!params.profile.settings) { + throw new AwsError('Profile settings not found when assuming role.', AwsErrorCodes.E_INVALID_PROFILE) + } + + // Try to get the STS credentials from cache + let result: IamCredentials + const credential = await params.stsCache + .getStsCredential(convertProfileToId(params.profile)) + .catch(_ => undefined) + + if (credential) { + result = credential + } else if (params.callStsOnInvalidIamCredential) { + // Generate STS credentials + result = await this.generateStsCredential(params) + // Cache STS credentials + await params.stsCache.setStsCredential(convertProfileToId(params.profile), result) + } else { + // If we could not get the cached STS credential and cannot generate a new credential, give up + params.observability.logging.log( + 'STS credential not found an callStsOnInvalidIamCredential = false, returning no credential.' + ) + throw new AwsError('STS credential not found.', AwsErrorCodes.E_INVALID_STS_CREDENTIAL) + } + + // Set up auto-refresh if MFA is disabled + if (!params.profile.settings.mfa_serial) { + await params.stsAutoRefresher + .watch(convertProfileToId(params.profile), () => this.generateStsCredential(params)) + .catch(reason => { + params.observability.logging.log(`Unable to auto-refresh STS credentials. ${reason}`) + }) + } + + return result + } + + private async getParentCredential(params: IamFlowParams): Promise { + let parentCredentials: IamCredentials + if (params.profile.kinds.includes(ProfileKind.IamSourceProfileProfile)) { + // Get the source profile + const profileData = await params.profileStore.load() + const sourceName = params.profile.settings!.source_profile! + const sourceProfile = profileData.profiles.find(p => p.name === sourceName) + if (!sourceProfile) { + params.observability.logging.log(`Source profile ${sourceName} not found.`) + throw new AwsError(`Source profile ${sourceName} not found.`, AwsErrorCodes.E_PROFILE_NOT_FOUND) + } + // Obtain parent profile credentials if IamRoleSourceProfile chain isn't too long + if (params.recursionCount <= sourceProfileRecursionMax) { + const response = await this.getCredential({ + ...params, + profile: sourceProfile, + recursionCount: params.recursionCount + 1, + }) + parentCredentials = response.credentials + } else { + throw new AwsError('Source profile chain exceeded max length.', AwsErrorCodes.E_INVALID_PROFILE) + } + } else { + throw new AwsError('Source credentials not found', AwsErrorCodes.E_INVALID_PROFILE) + } + return parentCredentials + } + + private async generateStsCredential(params: IamFlowParams): Promise { + try { + // Set up AssumeRole input + const parentCredentials = await this.getParentCredential(params) + const stsClient = new STSClient({ + region: params.profile.settings?.region || this.defaultRegion, + credentials: parentCredentials, + }) + const assumeRoleInput: AssumeRoleCommandInput = { + RoleArn: params.profile.settings?.role_arn, + RoleSessionName: params.profile.settings?.role_session_name || `session-${Date.now()}`, + DurationSeconds: 3600, + } + + // Add MFA fields to assume role request if MultiFactorAuthPresent is required + const mfaRequired = await checkMfaRequired( + parentCredentials, + ['sts:AssumeRole'], + params.profile.settings?.region + ) + if (mfaRequired) { + const response = await this.requestMfa(params) + assumeRoleInput.SerialNumber = response.mfaSerial + assumeRoleInput.TokenCode = response.code + + // Add the MFA serial number to the profile + const updatedProfile = { + ...params.profile, + settings: { ...params.profile.settings, mfa_serial: response.mfaSerial }, + } + params.profileStore.save({ profiles: [updatedProfile], ssoSessions: [] }) + // Update params.profile to ensure STS cache key is generated with updated MFA serial number + params.profile = updatedProfile + } + + // Call AssumeRole API + const command = new AssumeRoleCommand(assumeRoleInput) + const { Credentials } = await stsClient.send(command) + if (!Credentials?.AccessKeyId || !Credentials.SecretAccessKey) { + throw new AwsError( + 'Failed to generate credentials for assumed role', + AwsErrorCodes.E_CANNOT_CREATE_STS_CREDENTIAL + ) + } + return { + accessKeyId: Credentials.AccessKeyId, + secretAccessKey: Credentials.SecretAccessKey, + sessionToken: Credentials.SessionToken, + expiration: Credentials.Expiration, + } + } catch (e) { + params.observability.logging.log(`Error generating STS credentials.`) + throw e + } + } + + // Request an MFA code from the language client + private async requestMfa(params: IamFlowParams): Promise { + const response = await params.handlers.sendGetMfaCode({ + profileName: params.profile.name, + mfaSerial: params.profile.settings?.mfa_serial, + }) + + if (!response.code) { + throw new AwsError( + 'MFA code required when assuming role with MultiFactorAuthPresent permission condition', + AwsErrorCodes.E_MFA_REQUIRED + ) + } + if (!response.mfaSerial) { + throw new AwsError( + 'MFA serial required when assuming role with MultiFactorAuthPresent permission condition', + AwsErrorCodes.E_MFA_REQUIRED + ) + } + + return response } } diff --git a/server/aws-lsp-identity/src/iam/utils.ts b/server/aws-lsp-identity/src/iam/utils.ts index 06467906a9..a5ef2bf1af 100644 --- a/server/aws-lsp-identity/src/iam/utils.ts +++ b/server/aws-lsp-identity/src/iam/utils.ts @@ -2,37 +2,103 @@ import { IAMClient, SimulatePrincipalPolicyCommand, SimulatePrincipalPolicyComma import { GetCallerIdentityCommand, STSClient } from '@aws-sdk/client-sts' import { AwsErrorCodes, + CancellationToken, GetMfaCodeParams, GetMfaCodeResult, IamCredentials, + Profile, + StsCredentialChangedParams, } from '@aws/language-server-runtimes/server-interface' -import { AwsError } from '@aws/lsp-core' +import { AwsError, Observability } from '@aws/lsp-core' +import { StsCache } from '../sts/cache/stsCache' +import { StsAutoRefresher } from '../sts/stsAutoRefresher' +import { ProfileStore } from '../language-server/profiles/profileService' + +const defaultRegion = 'us-east-1' + +export async function validatePermissions( + credentials: IamCredentials, + permissions: string[], + region?: string +): Promise { + const response = await simulatePermissions(credentials, permissions, region) + // If evaluation results are missing, assume caller does not have sufficient permissions + if (!response.EvaluationResults) { + return false + } + return response.EvaluationResults.every(result => result.EvalDecision === 'allowed') +} + +export async function checkMfaRequired( + credentials: IamCredentials, + permissions: string[], + region?: string +): Promise { + const response = await simulatePermissions(credentials, permissions, region) + // If evaluation results are missing, assume caller does not need MFA + if (!response.EvaluationResults) { + return false + } + return response.EvaluationResults?.some(result => + result?.MissingContextValues?.includes('aws:MultiFactorAuthPresent') + ) +} + +export function throwOnInvalidCredentialId(iamCredentialId?: string): asserts iamCredentialId is string { + if (typeof iamCredentialId?.trim !== 'function' || !iamCredentialId?.trim()) { + throw new AwsError('IAM credential id is invalid.', AwsErrorCodes.E_INVALID_STS_CREDENTIAL) + } +} // Simulate permissions on the identity associated with the credentials -export async function simulatePermissions( +async function simulatePermissions( credentials: IamCredentials, permissions: string[], region?: string ): Promise { // Convert the credentials into an identity - const stsClient = new STSClient({ region: region || 'us-east-1', credentials: credentials }) + const stsClient = new STSClient({ region: region || defaultRegion, credentials: credentials }) const identity = await stsClient.send(new GetCallerIdentityCommand({})) if (!identity.Arn) { - throw new AwsError('Caller identity ARN not found.', AwsErrorCodes.E_INVALID_PROFILE) + throw new AwsError('Caller identity ARN not found.', AwsErrorCodes.E_CALLER_IDENTITY_NOT_FOUND) } // Simulate permissions on the identity - const iamClient = new IAMClient({ region: region || 'us-east-1', credentials: credentials }) + const iamClient = new IAMClient({ region: region || defaultRegion, credentials: credentials }) return await iamClient.send( new SimulatePrincipalPolicyCommand({ - PolicySourceArn: identity.Arn, + PolicySourceArn: convertToIamArn(identity.Arn), ActionNames: permissions, }) ) } +// Converts an assumed role ARN into an IAM role ARN +function convertToIamArn(arn: string) { + if (arn.includes(':assumed-role/')) { + const parts = arn.split(':') + const roleName = parts[5].split('/')[1] + return `arn:aws:iam::${parts[4]}:role/${roleName}` + } else { + return arn + } +} + export type SendGetMfaCode = (params: GetMfaCodeParams) => Promise +export type SendStsCredentialChanged = (params: StsCredentialChangedParams) => void export type IamHandlers = { sendGetMfaCode: SendGetMfaCode } + +export type IamFlowParams = { + profile: Profile + callStsOnInvalidIamCredential: boolean + recursionCount: number + profileStore: ProfileStore + stsCache: StsCache + stsAutoRefresher: StsAutoRefresher + handlers: IamHandlers + token: CancellationToken + observability: Observability +} diff --git a/server/aws-lsp-identity/src/language-server/autoRefresher.ts b/server/aws-lsp-identity/src/language-server/autoRefresher.ts new file mode 100644 index 0000000000..7f7f72f3f1 --- /dev/null +++ b/server/aws-lsp-identity/src/language-server/autoRefresher.ts @@ -0,0 +1,46 @@ +import { Observability } from '@aws/lsp-core' + +export const invalidDelay: number = -1 +export const refreshWindowMillis: number = 5 * 60 * 1000 +export const retryCooldownWindowMillis: number = 30000 +const bufferedRefreshWindowMillis = refreshWindowMillis * 0.95 +const bufferedRetryCooldownWindowMillis = retryCooldownWindowMillis * 1.05 +const maxRefreshJitterMillis = 10000 +const maxRetryCooldownJitterMillis = 3000 + +export abstract class AutoRefresher implements Disposable { + protected readonly timeouts: Record = {} + + constructor(protected readonly observability: Observability) {} + + [Symbol.dispose](): void { + for (const key of Object.keys(this.timeouts)) { + this.unwatch(key) + } + } + + protected abstract unwatch(key: string): void + + getDelay(expiration: string): number { + const nowMillis = Date.now() + const expiresAtMillis = Date.parse(expiration) + let delayMillis: number + + if (nowMillis < expiresAtMillis - refreshWindowMillis) { + // Before refresh window, schedule to run in refresh window with jitter + delayMillis = expiresAtMillis - bufferedRefreshWindowMillis - nowMillis + delayMillis += Math.random() * maxRefreshJitterMillis // Jitter to mitigate race conditions + } else if (expiresAtMillis - refreshWindowMillis < nowMillis && nowMillis < expiresAtMillis) { + // In refresh window with time for a retry + delayMillis = bufferedRetryCooldownWindowMillis + delayMillis += Math.random() * maxRetryCooldownJitterMillis // Jitter to mitigate race conditions + } else { + // Otherwise, expired + this.observability.logging.log('SSO token has expired and will not be auto-refreshed.') + return invalidDelay + } + + this.observability.logging.log(`Auto-refreshing SSO token in ${delayMillis} milliseconds.`) + return delayMillis + } +} diff --git a/server/aws-lsp-identity/src/language-server/identityServer.ts b/server/aws-lsp-identity/src/language-server/identityServer.ts index 31a01577de..1ca20798ef 100644 --- a/server/aws-lsp-identity/src/language-server/identityServer.ts +++ b/server/aws-lsp-identity/src/language-server/identityServer.ts @@ -7,20 +7,25 @@ import { AwsErrorCodes, GetSsoTokenParams, InvalidateSsoTokenParams, + InvalidateStsCredentialParams, InitializeParams, PartialInitializeResult, ShowMessageRequestParams, GetIamCredentialParams, GetMfaCodeParams, + StsCredentialChangedParams, + SsoTokenChangedParams, } from '@aws/language-server-runtimes/server-interface' import { SharedConfigProfileStore } from './profiles/sharedConfigProfileStore' import { IdentityService } from './identityService' import { FileSystemSsoCache, RefreshingSsoCache } from '../sso/cache' -import { SsoTokenAutoRefresher } from './ssoTokenAutoRefresher' +import { SsoTokenAutoRefresher } from '../sso/ssoTokenAutoRefresher' +import { FileSystemStsCache } from '../sts/cache/fileSystemStsCache' +import { StsAutoRefresher } from '../sts/stsAutoRefresher' import { AwsError, ServerBase } from '@aws/lsp-core' import { Features } from '@aws/language-server-runtimes/server-interface/server' -import { ShowUrl, ShowMessageRequest, ShowProgress } from '../sso/utils' -import { SendGetMfaCode } from '../iam/utils' +import { ShowUrl, ShowMessageRequest, ShowProgress, SendSsoTokenChanged } from '../sso/utils' +import { SendGetMfaCode, SendStsCredentialChanged } from '../iam/utils' import { IamProvider } from '../iam/iamProvider' export class IdentityServer extends ServerBase { @@ -45,23 +50,32 @@ export class IdentityServer extends ServerBase { const sendGetMfaCode: SendGetMfaCode = (params: GetMfaCodeParams) => this.features.identityManagement.sendGetMfaCode(params) + // Callbacks for client->server JSON-RPC calls + const sendSsoTokenChanged: SendSsoTokenChanged = (params: SsoTokenChangedParams) => + this.features.identityManagement.sendSsoTokenChanged(params) + const sendStsCredentialChanged: SendStsCredentialChanged = (params: StsCredentialChangedParams) => + this.features.identityManagement.sendStsCredentialChanged(params) + // Initialize dependencies const profileStore = new SharedConfigProfileStore(this.observability) const ssoCache = new RefreshingSsoCache( new FileSystemSsoCache(this.observability), - this.features.identityManagement.sendSsoTokenChanged, + sendSsoTokenChanged, this.observability ) const autoRefresher = new SsoTokenAutoRefresher(ssoCache, this.observability) - - const iamProvider = new IamProvider(this.observability, profileStore) + const stsCache = new FileSystemStsCache(this.observability) + const stsAutoRefresher = new StsAutoRefresher(stsCache, sendStsCredentialChanged, this.observability) + const iamProvider = new IamProvider() const identityService = new IdentityService( profileStore, ssoCache, autoRefresher, + stsCache, + stsAutoRefresher, iamProvider, { showUrl, showMessageRequest, showProgress, sendGetMfaCode }, this.getClientName(params), @@ -95,6 +109,14 @@ export class IdentityServer extends ServerBase { }) ) + this.features.identityManagement.onInvalidateStsCredential( + async (params: InvalidateStsCredentialParams, token: CancellationToken) => + await identityService.invalidateStsCredential(params, token).catch(reason => { + this.observability.logging.log(`InvalidateIamCredentials failed. ${reason}`) + throw awsResponseErrorWrap(reason) + }) + ) + this.features.identityManagement.onListProfiles( async (params: ListProfilesParams, token: CancellationToken) => await profileService.listProfiles(params, token).catch(reason => { @@ -112,6 +134,7 @@ export class IdentityServer extends ServerBase { ) this.disposables.push(autoRefresher) + this.disposables.push(stsAutoRefresher) return { ...result, diff --git a/server/aws-lsp-identity/src/language-server/identityService.test.ts b/server/aws-lsp-identity/src/language-server/identityService.test.ts index a007aead95..51966100ee 100644 --- a/server/aws-lsp-identity/src/language-server/identityService.test.ts +++ b/server/aws-lsp-identity/src/language-server/identityService.test.ts @@ -3,20 +3,23 @@ import { StubbedInstance, stubInterface } from 'ts-sinon' import { awsBuilderIdReservedName, SsoCache, SsoClientRegistration } from '../sso' import { IdentityService } from './identityService' import { ProfileData, ProfileStore } from './profiles/profileService' -import { SsoTokenAutoRefresher } from './ssoTokenAutoRefresher' -import { createStubInstance, restore, spy, SinonSpy, stub } from 'sinon' +import { SsoTokenAutoRefresher } from '../sso/ssoTokenAutoRefresher' +import { createStubInstance, restore, spy, SinonSpy, stub, SinonStub } from 'sinon' import { AuthorizationFlowKind, CancellationToken, + IamCredential, + IamCredentials, ProfileKind, SsoTokenSourceKind, } from '@aws/language-server-runtimes/protocol' import { SSOToken } from '@smithy/shared-ini-file-loader' import { Logging, Telemetry } from '@aws/language-server-runtimes/server-interface' import { Observability } from '@aws/lsp-core' -import { STSClient } from '@aws-sdk/client-sts' -import { IAMClient } from '@aws-sdk/client-iam' +import { StsCache } from '../sts/cache/stsCache' +import { StsAutoRefresher } from '../sts/stsAutoRefresher' import { IamProvider } from '../iam/iamProvider' +import * as iamUtils from '../iam/utils' // eslint-disable-next-line use(require('chai-as-promised')) @@ -25,10 +28,16 @@ let sut: IdentityService let profileStore: StubbedInstance let ssoCache: StubbedInstance +let stsCache: StubbedInstance let autoRefresher: StubbedInstance +let stsAutoRefresher: StubbedInstance let iamProvider: StubbedInstance let observability: StubbedInstance let authFlowFn: SinonSpy +let validatePermissionsStub: SinonStub< + [credentials: IamCredentials, permissions: string[], region?: string | undefined], + Promise +> describe('IdentityService', () => { beforeEach(() => { @@ -50,39 +59,6 @@ describe('IdentityService', () => { aws_secret_access_key: 'my-secret-key', }, }, - { - kinds: [ProfileKind.IamCredentialsProfile], - name: 'my-sts-profile', - settings: { - aws_access_key_id: 'my-access-key', - aws_secret_access_key: 'my-secret-key', - aws_session_token: 'my-session-token', - }, - }, - { - kinds: [ProfileKind.IamSourceProfileProfile], - name: 'my-role-profile', - settings: { - role_arn: 'my-role-arn', - source_profile: 'my-iam-profile', - }, - }, - { - kinds: [ProfileKind.IamSourceProfileProfile], - name: 'my-mfa-profile', - settings: { - role_arn: 'my-role-arn', - source_profile: 'my-iam-profile', - mfa_serial: 'my-device-arn', - }, - }, - { - kinds: [ProfileKind.IamCredentialProcessProfile], - name: 'my-process-profile', - settings: { - credential_process: 'my-process', - }, - }, ], ssoSessions: [ { @@ -110,18 +86,32 @@ describe('IdentityService', () => { setSsoToken: Promise.resolve(), }) + stsCache = stubInterface({ + getStsCredential: Promise.resolve(undefined), + setStsCredential: Promise.resolve(), + removeStsCredential: Promise.resolve(), + }) + autoRefresher = createStubInstance(SsoTokenAutoRefresher, { watch: Promise.resolve(), unwatch: undefined, }) as StubbedInstance - iamProvider = stubInterface({ + stsAutoRefresher = createStubInstance(StsAutoRefresher, { + watch: Promise.resolve(), + unwatch: undefined, + }) as StubbedInstance + + iamProvider = createStubInstance(IamProvider, { getCredential: Promise.resolve({ - accessKeyId: 'my-access-key', - secretAccessKey: 'my-secret-key', - sessionToken: 'my-session-token', - }), - }) + id: 'id', + kinds: [], + credentials: { + accessKeyId: 'access-key', + secretAccessKey: 'secret-key', + }, + } as IamCredential), + }) as StubbedInstance authFlowFn = spy(() => Promise.resolve({ @@ -138,12 +128,14 @@ describe('IdentityService', () => { profileStore, ssoCache, autoRefresher, + stsCache, + stsAutoRefresher, iamProvider, { showUrl: _ => {}, showMessageRequest: _ => Promise.resolve({ title: 'client-response' }), showProgress: _ => Promise.resolve(), - sendGetMfaCode: () => Promise.resolve({ code: 'mfa-code' }), + sendGetMfaCode: () => Promise.resolve({ code: 'mfa-code', mfaSerial: 'mfa-serial' }), }, 'My Client', observability, @@ -153,23 +145,8 @@ describe('IdentityService', () => { } ) - stub(STSClient.prototype, 'send').resolves({ - Credentials: { - AccessKeyId: 'role-access-key', - SecretAccessKey: 'role-secret-key', - SessionToken: 'role-session-token', - Expiration: new Date('2024-09-25T18:09:20.455Z'), - }, - AssumedRoleUser: { - Arn: 'role-arn', - AssumedRoleId: 'role-id', - }, - Arn: 'role-arn', - }) - - stub(IAMClient.prototype, 'send').resolves({ - EvaluationResults: [], - }) + validatePermissionsStub = stub(iamUtils, 'validatePermissions') + validatePermissionsStub.resolves(true) }) afterEach(() => { @@ -329,12 +306,20 @@ describe('IdentityService', () => { }) describe('getIamCredential', () => { - it('Can login with access key, secret key, and session token.', async () => { - const actual = await sut.getIamCredential({ profileName: 'my-sts-profile' }, CancellationToken.None) + it('Can login with IAM credentials.', async () => { + const actual = await sut.getIamCredential({ profileName: 'my-iam-profile' }, CancellationToken.None) + + expect(actual.credential.credentials.accessKeyId).to.equal('access-key') + expect(actual.credential.credentials.secretAccessKey).to.equal('secret-key') + }) - expect(actual.credential.credentials.accessKeyId).to.equal('my-access-key') - expect(actual.credential.credentials.secretAccessKey).to.equal('my-secret-key') - expect(actual.credential.credentials.sessionToken).to.equal('my-session-token') + it('Throws when permissions are insufficient', async () => { + validatePermissionsStub.resolves(false) + const error = await expect( + sut.getIamCredential({ profileName: 'my-iam-profile' }, CancellationToken.None) + ).rejectedWith(Error) + + expect(error.message).to.equal('Credentials have insufficient permissions.') }) }) @@ -351,4 +336,20 @@ describe('IdentityService', () => { expect(ssoCache.removeSsoToken.notCalled).is.true }) }) + + describe('invalidateStsCredential', () => { + it('Removes on valid name', async () => { + await sut.invalidateStsCredential({ iamCredentialId: 'my-role-profile' }, CancellationToken.None) + + expect(stsCache.removeStsCredential.called).is.true + }) + + it('Throws on invalid name', async () => { + await expect( + sut.invalidateStsCredential({ iamCredentialId: ' ' }, CancellationToken.None) + ).to.be.rejectedWith() + + expect(stsCache.removeStsCredential.notCalled).is.true + }) + }) }) diff --git a/server/aws-lsp-identity/src/language-server/identityService.ts b/server/aws-lsp-identity/src/language-server/identityService.ts index 22eecb3ca5..7f75df1645 100644 --- a/server/aws-lsp-identity/src/language-server/identityService.ts +++ b/server/aws-lsp-identity/src/language-server/identityService.ts @@ -12,15 +12,18 @@ import { IamIdentityCenterSsoTokenSource, InvalidateSsoTokenParams, InvalidateSsoTokenResult, + InvalidateStsCredentialParams, + InvalidateStsCredentialResult, MetricEvent, SsoSession, SsoTokenSourceKind, } from '@aws/language-server-runtimes/server-interface' - import { normalizeSettingList, ProfileStore } from './profiles/profileService' import { authorizationCodePkceFlow, awsBuilderIdReservedName, awsBuilderIdSsoRegion } from '../sso' import { SsoCache, SsoClientRegistration } from '../sso/cache' -import { SsoTokenAutoRefresher } from './ssoTokenAutoRefresher' +import { SsoTokenAutoRefresher } from '../sso/ssoTokenAutoRefresher' +import { StsCache } from '../sts/cache/stsCache' +import { StsAutoRefresher } from '../sts/stsAutoRefresher' import { throwOnInvalidClientRegistration, throwOnInvalidSsoSession, @@ -28,7 +31,7 @@ import { SsoFlowParams, SsoHandlers, } from '../sso/utils' -import { IamHandlers, simulatePermissions } from '../iam/utils' +import { IamFlowParams, IamHandlers, throwOnInvalidCredentialId, validatePermissions } from '../iam/utils' import { AwsError, Observability } from '@aws/lsp-core' import { __ServiceException } from '@aws-sdk/client-sso-oidc/dist-types/models/SSOOIDCServiceException' import { deviceCodeFlow } from '../sso/deviceCode/deviceCodeFlow' @@ -48,7 +51,9 @@ export class IdentityService { constructor( private readonly profileStore: ProfileStore, private readonly ssoCache: SsoCache, - private readonly autoRefresher: SsoTokenAutoRefresher, + private readonly ssoAutoRefresher: SsoTokenAutoRefresher, + private readonly stsCache: StsCache, + private readonly stsAutoRefresher: StsAutoRefresher, private readonly iamProvider: IamProvider, private readonly handlers: Handlers, private readonly clientName: string, @@ -103,7 +108,7 @@ export class IdentityService { clientName: this.clientName, clientRegistration, ssoSession, - handlers: this.handlers as Pick, + handlers: this.handlers as SsoHandlers, token, observability: this.observability, } @@ -129,7 +134,7 @@ export class IdentityService { } // Auto refresh is best effort - await this.autoRefresher.watch(this.clientName, ssoSession).catch(reason => { + await this.ssoAutoRefresher.watch(this.clientName, ssoSession).catch(reason => { this.observability.logging.log(`Unable to auto-refresh token. ${reason}`) }) @@ -168,12 +173,27 @@ export class IdentityService { throw new AwsError('Profile not found.', AwsErrorCodes.E_PROFILE_NOT_FOUND) } - const credentials = await this.iamProvider.getCredential(profile, options.callStsOnInvalidIamCredential) + const flowOpts: IamFlowParams = { + profile: profile, + callStsOnInvalidIamCredential: options.callStsOnInvalidIamCredential, + recursionCount: 0, + profileStore: this.profileStore, + stsCache: this.stsCache, + stsAutoRefresher: this.stsAutoRefresher, + handlers: this.handlers as IamHandlers, + token: token, + observability: this.observability, + } + const credential = await this.iamProvider.getCredential(flowOpts) // Validate permissions if (options.permissionSet.length > 0) { - const response = await simulatePermissions(credentials, options.permissionSet, profile.settings?.region) - if (!response?.EvaluationResults?.every(result => result.EvalDecision === 'allowed')) { + const hasPermissions = await validatePermissions( + credential.credentials, + options.permissionSet, + profile.settings?.region + ) + if (!hasPermissions) { throw new AwsError(`Credentials have insufficient permissions.`, AwsErrorCodes.E_INVALID_PROFILE) } } @@ -181,8 +201,8 @@ export class IdentityService { emitMetric('Succeeded') return { - credential: { id: params.profileName, kinds: profile.kinds, credentials: credentials }, - updateCredentialsParams: { data: credentials, encrypted: false }, + credential: credential, + updateCredentialsParams: { data: credential.credentials, encrypted: false }, } } catch (e) { emitMetric('Failed', e) @@ -208,7 +228,7 @@ export class IdentityService { try { throwOnInvalidSsoSessionName(params?.ssoTokenId) - this.autoRefresher.unwatch(params.ssoTokenId) + this.ssoAutoRefresher.unwatch(params.ssoTokenId) await this.ssoCache.removeSsoToken(params.ssoTokenId) @@ -222,6 +242,37 @@ export class IdentityService { } } + async invalidateStsCredential( + params: InvalidateStsCredentialParams, + token: CancellationToken + ): Promise { + const emitMetric = this.emitMetric.bind( + this, + 'flareIdentity_invalidateStsCredential', + this.invalidateStsCredential.name, + Date.now() + ) + + token.onCancellationRequested(_ => { + emitMetric('Cancelled') + }) + + try { + throwOnInvalidCredentialId(params.iamCredentialId) + + this.stsAutoRefresher.unwatch(params.iamCredentialId) + + await this.stsCache.removeStsCredential(params.iamCredentialId) + + emitMetric('Succeeded') + this.observability.logging.log('Successfully invalidated STS credentials.') + return {} + } catch (e) { + emitMetric('Failed', e) + throw e + } + } + private emitMetric( name: string, source: string, diff --git a/server/aws-lsp-identity/src/sso/cache/refreshingSsoCache.ts b/server/aws-lsp-identity/src/sso/cache/refreshingSsoCache.ts index d642f0a36b..197fd1680b 100644 --- a/server/aws-lsp-identity/src/sso/cache/refreshingSsoCache.ts +++ b/server/aws-lsp-identity/src/sso/cache/refreshingSsoCache.ts @@ -7,13 +7,11 @@ import { throwOnInvalidClientName, UpdateSsoTokenFromCreateToken, throwOnInvalidSsoSessionName, + SendSsoTokenChanged, } from '../utils' -import { RaiseSsoTokenChanged } from '../../language-server/ssoTokenAutoRefresher' import { CreateTokenCommandOutput, InvalidGrantException } from '@aws-sdk/client-sso-oidc' import { AwsError, Observability } from '@aws/lsp-core' - -export const refreshWindowMillis: number = 5 * 60 * 1000 -export const retryCooldownWindowMillis: number = 30000 +import { refreshWindowMillis, retryCooldownWindowMillis } from '../../language-server/autoRefresher' interface SsoTokenDetail { lastRefreshMillis: number @@ -24,7 +22,7 @@ export class RefreshingSsoCache implements SsoCache { constructor( private readonly next: SsoCache, - private readonly raiseSsoTokenChanged: RaiseSsoTokenChanged, + private readonly raiseSsoTokenChanged: SendSsoTokenChanged, private readonly observability: Observability ) {} @@ -128,14 +126,18 @@ export class RefreshingSsoCache implements SsoCache { // Current time is before start of refresh window? Just return it const refreshAfterMillis = accessTokenExpiresAtMillis - refreshWindowMillis if (nowMillis < refreshAfterMillis) { - this.observability.logging.log('SSO token before refresh window. Returning current SSO token.') + this.observability.logging.log( + 'SSO token expiration is before refresh window. Returning current SSO token.' + ) return ssoToken } // Last refresh attempt was less than the retry window? Just return it const retryAfterMillis = ssoTokenDetail.lastRefreshMillis + retryCooldownWindowMillis if (nowMillis < retryAfterMillis) { - this.observability.logging.log('SSO token in retry cooldown window. Returning current SSO token.') + this.observability.logging.log( + 'SSO token expiration is in retry cooldown window. Returning current SSO token.' + ) return ssoToken } } diff --git a/server/aws-lsp-identity/src/language-server/ssoTokenAutoRefresher.test.ts b/server/aws-lsp-identity/src/sso/ssoTokenAutoRefresher.test.ts similarity index 100% rename from server/aws-lsp-identity/src/language-server/ssoTokenAutoRefresher.test.ts rename to server/aws-lsp-identity/src/sso/ssoTokenAutoRefresher.test.ts diff --git a/server/aws-lsp-identity/src/language-server/ssoTokenAutoRefresher.ts b/server/aws-lsp-identity/src/sso/ssoTokenAutoRefresher.ts similarity index 54% rename from server/aws-lsp-identity/src/language-server/ssoTokenAutoRefresher.ts rename to server/aws-lsp-identity/src/sso/ssoTokenAutoRefresher.ts index 477d5c901f..4a3863129e 100644 --- a/server/aws-lsp-identity/src/language-server/ssoTokenAutoRefresher.ts +++ b/server/aws-lsp-identity/src/sso/ssoTokenAutoRefresher.ts @@ -1,30 +1,18 @@ -import { SsoSession, SsoTokenChangedParams } from '@aws/language-server-runtimes/protocol' -import { RefreshingSsoCache, refreshWindowMillis, retryCooldownWindowMillis } from '../sso/cache/refreshingSsoCache' -import { throwOnInvalidClientName, throwOnInvalidSsoSession, throwOnInvalidSsoSessionName } from '../sso/utils' +import { SsoSession } from '@aws/language-server-runtimes/protocol' +import { RefreshingSsoCache } from './cache/refreshingSsoCache' +import { throwOnInvalidClientName, throwOnInvalidSsoSession, throwOnInvalidSsoSessionName } from './utils' import { MetricEvent } from '@aws/language-server-runtimes/server-interface' -import { normalizeSettingList } from './profiles/profileService' +import { normalizeSettingList } from '../language-server/profiles/profileService' import { __ServiceException } from '@aws-sdk/client-sso-oidc/dist-types/models/SSOOIDCServiceException' import { AwsError, Observability } from '@aws/lsp-core' +import { AutoRefresher, invalidDelay } from '../language-server/autoRefresher' -const bufferedRefreshWindowMillis = refreshWindowMillis * 0.95 -const bufferedRetryCooldownWindowMillis = retryCooldownWindowMillis * 1.05 -const maxRefreshJitterMillis = 10000 -const maxRetryCooldownJitterMillis = 3000 - -export type RaiseSsoTokenChanged = (params: SsoTokenChangedParams) => void - -export class SsoTokenAutoRefresher implements Disposable { - private readonly timeouts: Record = {} - +export class SsoTokenAutoRefresher extends AutoRefresher { constructor( private readonly ssoCache: RefreshingSsoCache, - private readonly observability: Observability - ) {} - - [Symbol.dispose](): void { - for (const ssoSessionName of Object.keys(this.timeouts)) { - this.unwatch(ssoSessionName) - } + observability: Observability + ) { + super(observability) } async watch(clientName: string, ssoSession: SsoSession): Promise { @@ -46,29 +34,12 @@ export class SsoTokenAutoRefresher implements Disposable { return } - const nowMillis = Date.now() - const accessTokenExpiresAtMillis = Date.parse(ssoToken.expiresAt) - let delayMillis: number - - if (nowMillis < accessTokenExpiresAtMillis - refreshWindowMillis) { - // Before refresh window, schedule to run in refresh window with jitter - delayMillis = accessTokenExpiresAtMillis - bufferedRefreshWindowMillis - nowMillis - delayMillis += Math.random() * maxRefreshJitterMillis // Jitter to mitigate race conditions - } else if ( - accessTokenExpiresAtMillis - refreshWindowMillis < nowMillis && - nowMillis < accessTokenExpiresAtMillis - ) { - // In refresh window with time for a retry - delayMillis = bufferedRetryCooldownWindowMillis - delayMillis += Math.random() * maxRetryCooldownJitterMillis // Jitter to mitigate race conditions - } else { - // Otherwise, expired - this.observability.logging.log('SSO token has expired and will not be auto-refreshed.') - return + // Refresh timeout if delay is valid + const delayMillis = this.getDelay(ssoToken.expiresAt) + if (delayMillis !== invalidDelay) { + this.observability.logging.log(`Auto-refreshing SSO token in ${delayMillis} milliseconds.`) + this.timeouts[ssoSession.name] = setTimeout(this.watch.bind(this, clientName, ssoSession), delayMillis) } - - this.observability.logging.log(`Auto-refreshing SSO token in ${delayMillis} milliseconds.`) - this.timeouts[ssoSession.name] = setTimeout(this.watch.bind(this, clientName, ssoSession), delayMillis) } catch (e) { emitMetric(e, ssoSession) diff --git a/server/aws-lsp-identity/src/sso/utils.ts b/server/aws-lsp-identity/src/sso/utils.ts index 29841df6e4..38c6846b17 100644 --- a/server/aws-lsp-identity/src/sso/utils.ts +++ b/server/aws-lsp-identity/src/sso/utils.ts @@ -5,6 +5,7 @@ import { ShowMessageRequestParams, SsoSession, Lsp, + SsoTokenChangedParams, } from '@aws/language-server-runtimes/server-interface' import { CreateTokenCommandOutput, SSOOIDC, SSOOIDCClientConfig } from '@aws-sdk/client-sso-oidc' import { SsoClientRegistration } from './cache' @@ -36,7 +37,7 @@ export function getSsoOidc(ssoRegion: string): SSOOIDC & Disposable { } export function throwOnInvalidClientName(clientName?: string): asserts clientName is string { - if (!clientName?.trim().length) { + if (typeof clientName?.trim !== 'function' || !clientName?.trim().length) { throw new AwsError(`Client name [${clientName}] is invalid.`, AwsErrorCodes.E_INVALID_SSO_CLIENT) } } @@ -57,7 +58,7 @@ export function throwOnInvalidClientRegistration( } export function throwOnInvalidSsoSessionName(ssoSessionName?: string): asserts ssoSessionName is string { - if (!ssoSessionName?.trim()) { + if (typeof ssoSessionName?.trim !== 'function' || !ssoSessionName?.trim()) { throw new AwsError('SSO session name is invalid.', AwsErrorCodes.E_INVALID_SSO_SESSION) } } @@ -111,6 +112,7 @@ export function UpdateSsoTokenFromCreateToken( export type ShowUrl = (url: URL) => void export type ShowMessageRequest = (params: ShowMessageRequestParams) => Promise export type ShowProgress = Lsp['sendProgress'] +export type SendSsoTokenChanged = (params: SsoTokenChangedParams) => void export type SsoHandlers = { showUrl: ShowUrl showMessageRequest: ShowMessageRequest diff --git a/server/aws-lsp-identity/src/sts/cache/fileSystemStsCache.test.ts b/server/aws-lsp-identity/src/sts/cache/fileSystemStsCache.test.ts new file mode 100644 index 0000000000..c686ad8cd1 --- /dev/null +++ b/server/aws-lsp-identity/src/sts/cache/fileSystemStsCache.test.ts @@ -0,0 +1,197 @@ +// eslint-disable-next-line @typescript-eslint/no-require-imports +import mock = require('mock-fs') +import { FileSystemStsCache, getStsCredentialFilepath } from './fileSystemStsCache' +import { expect, use } from 'chai' +import { DirectoryItems } from 'mock-fs/lib/filesystem' +import { Logging, IamCredentials, Telemetry } from '@aws/language-server-runtimes/server-interface' +import { access } from 'fs/promises' +import * as fs from 'fs' +import { StubbedInstance, stubInterface } from 'ts-sinon' +import { Observability } from '@aws/lsp-core' + +// eslint-disable-next-line +use(require('chai-as-promised')) + +let sut: FileSystemStsCache + +let observability: StubbedInstance + +const id: string = 'someid' + +const credential: IamCredentials = { + accessKeyId: 'someaccesskeyid', + secretAccessKey: 'somesecretaccesskey', + sessionToken: 'somesessiontoken', + expiration: new Date(Date.now() + 60 * 60 * 1000), +} + +function setupTest(args?: { id?: string; credential?: IamCredentials }): void { + // Just for sanity, safe to call restore if mock not currently active + mock.restore() + + args = { ...{ id, credential }, ...args } + + const mockConfig: DirectoryItems = {} + mockConfig[getStsCredentialFilepath(args.id!)] = JSON.stringify({ + Credentials: { + AccessKeyId: credential.accessKeyId, + SecretAccessKey: credential.secretAccessKey, + SessionToken: credential.sessionToken, + Expiration: credential.expiration, + }, + }) + + mock(mockConfig) +} + +function expectFileExists(filename: string): Chai.Assertion { + return expect(access(filename, fs.constants.F_OK)) +} + +describe('FileSystemStsCache', () => { + beforeEach(() => { + observability = stubInterface() + observability.logging = stubInterface() + observability.telemetry = stubInterface() + + sut = new FileSystemStsCache(observability) + }) + + afterEach(() => { + mock.restore() + }) + + it('removeStsCredential deletes a valid credential', async () => { + const filename = getStsCredentialFilepath(id) + setupTest() + + await expectFileExists(filename).to.not.be.rejectedWith() + + await sut.removeStsCredential(id) + + await expectFileExists(filename).to.be.rejectedWith() + }) + + it('removeStsCredential does nothing on invalid/non-existent credential', async () => { + const filename = getStsCredentialFilepath(id) + setupTest() + + await expectFileExists(filename).to.not.be.rejectedWith() + + await sut.removeStsCredential('non-existent credential') + + await expectFileExists(filename).to.not.be.rejectedWith() + }) + + it('removeStsCredential throws on invalid id', async () => { + await expect(sut.removeStsCredential(' ')).to.be.rejectedWith() + }) + + it('getStsCredential returns valid credential', async () => { + setupTest() + + const actual = await sut.getStsCredential(id) + + expect(actual).to.not.be.null.and.not.undefined + expect(actual?.accessKeyId).to.equal(credential.accessKeyId) + expect(actual?.secretAccessKey).to.equal(credential.secretAccessKey) + expect(actual?.sessionToken).to.equal(credential.sessionToken) + expect(actual?.expiration?.toISOString()).to.equal(credential.expiration?.toISOString()) + }) + + it('getStsCredential returns undefined when file does not exist', async () => { + setupTest() + + const actual = await sut.getStsCredential('does not exist') + + expect(actual).to.be.undefined + }) + + it('getStsCredential returns undefined on invalid credential', async () => { + setupTest({ id: 'invalid-id', credential: {} as IamCredentials }) + + const actual = await sut.getStsCredential(id) + + expect(actual).to.be.undefined + }) + + it('getStsCredential returns undefined on expired credential', async () => { + setupTest({ + id: 'invalid-id', + credential: { + accessKeyId: 'newaccesskeyid', + secretAccessKey: 'newsecretaccesskey', + sessionToken: 'newsessiontoken', + expiration: new Date(Date.now() - 60 * 60 * 1000), + } as IamCredentials, + }) + + const actual = await sut.getStsCredential(id) + + expect(actual).to.be.undefined + }) + + it('setStsCredential writes new valid credential', async () => { + setupTest() + await sut.setStsCredential(id, credential) + + const actual = await sut.getStsCredential(id) + + expect(actual).to.not.be.null.and.not.undefined + expect(actual?.accessKeyId).to.equal(credential.accessKeyId) + expect(actual?.secretAccessKey).to.equal(credential.secretAccessKey) + expect(actual?.sessionToken).to.equal(credential.sessionToken) + expect(actual?.expiration?.toISOString()).to.equal(credential.expiration?.toISOString()) + }) + + it('setStsCredential writes new valid credential when ~/.aws does not exist', async () => { + mock.restore() + mock({}) + + await sut.setStsCredential(id, credential) + + const actual = await sut.getStsCredential(id) + + expect(actual).to.not.be.null.and.not.undefined + expect(actual?.accessKeyId).to.equal(credential.accessKeyId) + expect(actual?.secretAccessKey).to.equal(credential.secretAccessKey) + expect(actual?.sessionToken).to.equal(credential.sessionToken) + expect(actual?.expiration?.toISOString()).to.equal(credential.expiration?.toISOString()) + }) + + it('setStsCredential writes updated existing credential', async () => { + setupTest() + + const newCredential = { + accessKeyId: 'newaccesskeyid', + secretAccessKey: 'newsecretaccesskey', + sessionToken: 'newsessiontoken', + expiration: new Date(Date.now() + 60 * 60 * 1000), + } + await sut.setStsCredential(id, newCredential) + const actual = await sut.getStsCredential(id) + + expect(actual).to.not.be.null.and.not.undefined + expect(actual?.accessKeyId).to.equal(newCredential.accessKeyId) + expect(actual?.secretAccessKey).to.equal(newCredential.secretAccessKey) + expect(actual?.sessionToken).to.equal(newCredential.sessionToken) + expect(actual?.expiration?.toISOString()).to.equal(newCredential.expiration.toISOString()) + }) + + it('setStsCredential returns without error on invalid credential', async () => { + setupTest() + + await sut.setStsCredential(id, {} as IamCredentials) // no throw + }) + + it('setStsCredential returns without error on expired credential', async () => { + setupTest() + + await sut.setStsCredential(id, { + accessKeyId: 'newaccesskeyid', + secretAccessKey: 'newsecretaccesskey', + sessionToken: 'newsessiontoken', + expiration: new Date(Date.now() - 60 * 60 * 1000), + } as IamCredentials) // no throw + }) +}) diff --git a/server/aws-lsp-identity/src/sts/cache/fileSystemStsCache.ts b/server/aws-lsp-identity/src/sts/cache/fileSystemStsCache.ts new file mode 100644 index 0000000000..a11120f232 --- /dev/null +++ b/server/aws-lsp-identity/src/sts/cache/fileSystemStsCache.ts @@ -0,0 +1,136 @@ +import { StsCache } from './stsCache' +import { AwsError, Observability } from '@aws/lsp-core' +import { AwsErrorCodes, IamCredentials, Profile } from '@aws/language-server-runtimes/protocol' +import path, { join } from 'path' +import { mkdir, readFile, unlink, writeFile } from 'fs/promises' +import { getHomeDir } from '@smithy/shared-ini-file-loader' +import { throwOnInvalidCredentialId } from '../../iam/utils' +import { createHash } from 'crypto' + +export class FileSystemStsCache implements StsCache { + constructor(private readonly observability: Observability) {} + + async removeStsCredential(name: string): Promise { + throwOnInvalidCredentialId(name) + + await unlink(getStsCredentialFilepath(name)).catch(reason => this.ignoreDoesNotExistOrThrow(reason)) + } + + async getStsCredential(name: string): Promise { + try { + let credential = await getStsCredentialFromFile(name) + if (!this.isValid(credential)) { + this.observability.logging.log(`Cannot get credential from ${name}: missing fields.`) + return undefined + } + // Ensure expiration is a Date object + if (typeof credential.expiration === 'string') { + credential = { ...credential, expiration: new Date(credential.expiration) } + } + if (this.isExpired(credential)) { + this.observability.logging.log(`Credential from ${name} is expired`) + return undefined + } + return credential + } catch (e) { + this.ignoreDoesNotExistOrThrow(e) + } + } + + async setStsCredential(name: string, credential: IamCredentials): Promise { + if (!this.isValid(credential)) { + this.observability.logging.log('Cannot set credential: missing fields.') + return + } + if (this.isExpired(credential)) { + this.observability.logging.log(`Cannot set credential: expired`) + return undefined + } + + await writeStsObjectToFile(name, credential).catch(reason => { + throw AwsError.wrap(reason, AwsErrorCodes.E_CANNOT_WRITE_SSO_CACHE) + }) + } + + private ignoreDoesNotExistOrThrow(error: unknown): Promise { + // Error codes are consistent across OSes (Windows is converted to libuv error codes) + // https://nodejs.org/api/errors.html#errorerrno + if ((error as SystemError)?.code === 'ENOENT') { + return Promise.resolve(undefined) + } + + this.observability.logging.log('Cannot read STS cache.') + throw AwsError.wrap(error as Error, AwsErrorCodes.E_CANNOT_READ_SSO_CACHE) + } + + private isValid(credential: IamCredentials): boolean { + return ( + credential.accessKeyId !== undefined && + credential.secretAccessKey !== undefined && + credential.sessionToken !== undefined && + credential.expiration !== undefined + ) + } + + private isExpired(credential: IamCredentials): boolean { + if (credential.expiration === undefined) { + return false + } + return Date.now() >= credential.expiration.getTime() + } +} + +export function convertProfileToId(profile: Profile) { + const key = JSON.stringify({ + RoleArn: profile.settings?.role_arn, + RoleSessionName: profile.settings?.role_session_name, + SerialNumber: profile.settings?.mfa_serial, + }) + return createHash('sha1').update(key).digest('hex') +} + +// Based on: +// https://github.com/smithy-lang/smithy-typescript/blob/main/packages/shared-ini-file-loader/src/getSSOTokenFilepath.ts +export function getStsCredentialFilepath(id: string): string { + return join(getHomeDir(), '.aws', 'flare', 'cache', `${id}.json`) +} + +// Based on: +// https://github.com/smithy-lang/smithy-typescript/blob/main/packages/shared-ini-file-loader/src/getSSOTokenFromFile.ts +async function getStsCredentialFromFile(id: string): Promise { + const stsCredentialFilepath = getStsCredentialFilepath(id) + const text = await readFile(stsCredentialFilepath, 'utf8') + const json = JSON.parse(text) + return { + accessKeyId: json.Credentials.AccessKeyId, + secretAccessKey: json.Credentials.SecretAccessKey, + sessionToken: json.Credentials.SessionToken, + expiration: json.Credentials.Expiration, + } as IamCredentials +} + +// Based on: +// https://github.com/aws/aws-sdk-js-v3/blob/6e61f0e78ff7a9e3b1f2cd651bde5fc656d85ba9/packages/token-providers/src/writeSSOTokenToFile.ts +async function writeStsObjectToFile(id: string, credentials: IamCredentials): Promise { + const filepath = getStsCredentialFilepath(id) + await mkdir(path.dirname(filepath), { mode: 0o755, recursive: true }) + const json = JSON.stringify( + { + Credentials: { + AccessKeyId: credentials.accessKeyId, + SecretAccessKey: credentials.secretAccessKey, + SessionToken: credentials.sessionToken, + Expiration: credentials.expiration, + }, + }, + null, + 2 + ) + return await writeFile(filepath, json, { encoding: 'utf-8', flush: true, mode: 0o600 }) +} + +// Minimal declaration of SystemError (no node type declaration for it) to access code property +// https://nodejs.org/api/errors.html#class-systemerror +interface SystemError { + code: string +} diff --git a/server/aws-lsp-identity/src/sts/cache/stsCache.ts b/server/aws-lsp-identity/src/sts/cache/stsCache.ts new file mode 100644 index 0000000000..d89e8dbc4e --- /dev/null +++ b/server/aws-lsp-identity/src/sts/cache/stsCache.ts @@ -0,0 +1,7 @@ +import { IamCredentials } from '@aws/language-server-runtimes/protocol' + +export interface StsCache { + getStsCredential(name: string): Promise + setStsCredential(name: string, credentials: IamCredentials): Promise + removeStsCredential(name: string): Promise +} diff --git a/server/aws-lsp-identity/src/sts/stsAutoRefresher.test.ts b/server/aws-lsp-identity/src/sts/stsAutoRefresher.test.ts new file mode 100644 index 0000000000..ec080b6ba1 --- /dev/null +++ b/server/aws-lsp-identity/src/sts/stsAutoRefresher.test.ts @@ -0,0 +1,124 @@ +import { expect, use } from 'chai' +import { StsAutoRefresher } from './stsAutoRefresher' +import { StubbedInstance, stubInterface } from 'ts-sinon' +import { restore, spy } from 'sinon' +import { AwsErrorCodes, IamCredentials, Logging, Telemetry } from '@aws/language-server-runtimes/server-interface' +import { AwsError, Observability } from '@aws/lsp-core' +import { FileSystemStsCache } from './cache/fileSystemStsCache' + +// eslint-disable-next-line +use(require('chai-as-promised')) + +let observability: StubbedInstance + +const profileName = 'someprofile' +const now = Date.now() + +function createStsCredential(expiresAsOffsetMillis: number): IamCredentials { + return { + accessKeyId: 'someaccesskeyid', + secretAccessKey: 'somesecretaccesskey', + sessionToken: 'somesessiontoken', + expiration: new Date(now + expiresAsOffsetMillis), + } satisfies IamCredentials +} + +function refreshStsCredential(): Promise { + return Promise.resolve({ + accessKeyId: 'newaccesskeyid', + secretAccessKey: 'newsecretaccesskey', + sessionToken: 'newsessiontoken', + expiration: new Date(now + 60 * 60 * 1000 /* 1 hour in relative seconds */), + } satisfies IamCredentials) +} + +function stubStsCache(credential?: IamCredentials): FileSystemStsCache { + return stubInterface({ + getStsCredential: credential + ? Promise.resolve(credential) + : Promise.reject(new AwsError('Test: No STS credential', AwsErrorCodes.E_INVALID_STS_CREDENTIAL)), + }) +} + +describe('StsAutoRefresher', () => { + beforeEach(() => { + observability = stubInterface() + observability.logging = stubInterface() + observability.telemetry = stubInterface() + }) + + afterEach(() => { + restore() + }) + + it('watch does nothing if STS credential is not loaded from cache.', async () => { + const stsCache = stubStsCache() + using sut = new StsAutoRefresher(stsCache, () => {}, observability) + + expect(Object.keys(sut['timeouts']).length).to.equal(0) + + await sut.watch(profileName, refreshStsCredential) + + expect(Object.keys(sut['timeouts']).length).to.equal(0) + }) + + it('watch does nothing if STS credential is expired.', async () => { + const stsCache = stubStsCache(createStsCredential(-10000)) + using sut = new StsAutoRefresher(stsCache, () => {}, observability) + + expect(Object.keys(sut['timeouts']).length).to.equal(0) + + await sut.watch(profileName, refreshStsCredential) + + expect(Object.keys(sut['timeouts']).length).to.equal(0) + }) + + it('watch schedules refresh in refresh window prior to expiration.', async () => { + const setTimeoutSpy = spy(global, 'setTimeout') + + // Before the refresh window + const stsCache = stubStsCache(createStsCredential(60 * 60 * 1000)) + using sut = new StsAutoRefresher(stsCache, () => {}, observability) + + expect(Object.keys(sut['timeouts']).length).to.equal(0) + + await sut.watch(profileName, refreshStsCredential) + + expect(Object.keys(sut['timeouts']).length).to.equal(1) + + expect(setTimeoutSpy.calledOnce).to.be.true + expect(setTimeoutSpy.lastCall.args[1]) + .to.be.greaterThan(55 * 60 * 1000) + .and.lessThan(60 * 60 * 1000) + }) + + it('watch schedules refresh retry in retry window after last attempt.', async () => { + const setTimeoutSpy = spy(global, 'setTimeout') + + // In the refresh window + const stsCache = stubStsCache(createStsCredential(4 * 60 * 1000)) + using sut = new StsAutoRefresher(stsCache, () => {}, observability) + + expect(Object.keys(sut['timeouts']).length).to.equal(0) + + await sut.watch(profileName, refreshStsCredential) + + expect(Object.keys(sut['timeouts']).length).to.equal(1) + + expect(setTimeoutSpy.calledOnce).to.be.true + expect(setTimeoutSpy.lastCall.args[1]) + .to.be.greaterThan(30 * 1000) + .and.lessThan(40 * 1000) + }) + + it('unwatch does nothing if profileName is not watched.', () => { + const stsCache = stubStsCache() + using sut = new StsAutoRefresher(stsCache, () => {}, observability) + + expect(Object.keys(sut['timeouts']).length).to.equal(0) + + sut.unwatch(refreshStsCredential.name) + + expect(Object.keys(sut['timeouts']).length).to.equal(0) + }) +}) diff --git a/server/aws-lsp-identity/src/sts/stsAutoRefresher.ts b/server/aws-lsp-identity/src/sts/stsAutoRefresher.ts new file mode 100644 index 0000000000..e5610a8c7e --- /dev/null +++ b/server/aws-lsp-identity/src/sts/stsAutoRefresher.ts @@ -0,0 +1,84 @@ +import { StsCache } from './cache/stsCache' +import { Observability } from '@aws/lsp-core' +import { AutoRefresher, invalidDelay } from '../language-server/autoRefresher' +import { IamCredentials, StsCredentialChangedKind } from '@aws/language-server-runtimes/protocol' +import { SendStsCredentialChanged } from '../iam/utils' + +interface StsCredentialDetail { + lastRefreshMillis: number +} + +export class StsAutoRefresher extends AutoRefresher { + private readonly stsCredentialDetails: Record = {} + + constructor( + private readonly stsCache: StsCache, + private readonly raiseStsCredentialChanged: SendStsCredentialChanged, + observability: Observability + ) { + super(observability) + } + + async watch(iamCredentialId: string, refreshCallback: () => Promise): Promise { + try { + this.unwatch(iamCredentialId) + + const credential = await this.stsCache.getStsCredential(iamCredentialId).catch(_ => undefined) + + if (!credential?.expiration) { + this.observability.logging.log( + 'STS credentials do not exist or have no expiration, will not be auto-refreshed.' + ) + return + } + + // Get or create StsCredentialDetail + const stsCredentialDetail = + this.stsCredentialDetails[iamCredentialId] ?? + (this.stsCredentialDetails[iamCredentialId] = { lastRefreshMillis: 0 }) + + const delayMillis = this.getDelay(credential.expiration.toISOString()) + if (delayMillis !== invalidDelay) { + this.observability.logging.info(`Auto-refreshing STS credentials in ${delayMillis} milliseconds.`) + this.timeouts[iamCredentialId] = setTimeout(async () => { + try { + // Update last refresh attempt time (matching SSO pattern) + stsCredentialDetail.lastRefreshMillis = Date.now() + + // Passing refresh function into here is easier than refreshing from STS cache + const newCredentials = await refreshCallback() + this.observability.logging.log(`Generated new STS credentials`) + await this.stsCache.setStsCredential(iamCredentialId, newCredentials) + + // Continue watching with the new credentials (allows multiple refreshes) + this.watch(iamCredentialId, refreshCallback) + + this.raiseStsCredentialChanged({ + kind: StsCredentialChangedKind.Refreshed, + stsCredentialId: iamCredentialId, + }) + } catch (error) { + this.observability.logging.log(`Failed to refresh STS credentials: ${error}`) + + // On error, continue watching to retry later (matching SSO pattern) + this.watch(iamCredentialId, refreshCallback) + } + }, delayMillis) + } + } catch (e) { + this.observability.logging.log(`Error setting up STS auto-refresh: ${e}`) + throw e + } + } + + unwatch(stsSessionName: string): void { + const timeout = this.timeouts[stsSessionName] + if (timeout) { + clearTimeout(timeout) + delete this.timeouts[stsSessionName] + // Also clean up the credential detail + delete this.stsCredentialDetails[stsSessionName] + this.observability.logging.log('STS credentials unwatched and will not be auto-refreshed.') + } + } +}