diff --git a/package-lock.json b/package-lock.json index 375eb9dcdd..b8ff1c22b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2538,6 +2538,18 @@ "tweetnacl": "^1.0.3" } }, + "node_modules/@google-cloud/secret-manager": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@google-cloud/secret-manager/-/secret-manager-5.6.0.tgz", + "integrity": "sha512-0daW/OXQEVc6VQKPyJTQNyD+563I/TYQ7GCQJx4dq3lB666R9FUPvqHx9b/o/qQtZ5pfuoCbGZl3krpxgTSW8Q==", + "license": "Apache-2.0", + "dependencies": { + "google-gax": "^4.0.3" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@grpc/grpc-js": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.0.tgz", @@ -7613,7 +7625,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "dev": true, "license": "MIT", "engines": { "node": ">= 10" @@ -7719,7 +7730,6 @@ "version": "0.12.5", "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", - "dev": true, "license": "MIT" }, "node_modules/@types/chai": { @@ -8074,6 +8084,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "license": "MIT" + }, "node_modules/@types/marked": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@types/marked/-/marked-5.0.2.tgz", @@ -8290,7 +8306,6 @@ "version": "2.48.12", "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", - "dev": true, "license": "MIT", "dependencies": { "@types/caseless": "*", @@ -8367,7 +8382,6 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", - "dev": true, "license": "MIT" }, "node_modules/@types/trusted-types": { @@ -9953,6 +9967,15 @@ "dev": true, "license": "MIT" }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -10247,7 +10270,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/buffer-fill": { @@ -12291,6 +12313,18 @@ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", "license": "MIT" }, + "node_modules/duplexify": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", + "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.2" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -12301,7 +12335,6 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" @@ -13815,6 +13848,12 @@ "node": ">=4" } }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, "node_modules/extract-zip": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", @@ -14181,7 +14220,6 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", - "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -14381,6 +14419,70 @@ "dev": true, "license": "MIT" }, + "node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gaxios/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/gaxios/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/gaxios/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gcp-metadata": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -14593,6 +14695,76 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/google-auth-library": { + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-auth-library/node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/google-auth-library/node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/google-gax": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.4.1.tgz", + "integrity": "sha512-Phyp9fMfA00J3sZbJxbbB4jC55b7DBjE3F6poyL3wKMEBVKA79q6BGuHcTiM28yOzVql0NDbRL8MLLh8Iwk9Dg==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.10.9", + "@grpc/proto-loader": "^0.7.13", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "google-auth-library": "^9.3.0", + "node-fetch": "^2.7.0", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^2.0.2", + "protobufjs": "^7.3.2", + "retry-request": "^7.0.0", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-logging-utils": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, "node_modules/google-protobuf": { "version": "3.21.4", "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.4.tgz", @@ -14728,6 +14900,40 @@ "dev": true, "license": "Standard 'no charge' license: https://gsap.com/standard-license. Club GSAP members get more: https://gsap.com/licensing/. Why GreenSock doesn't employ an MIT license: https://gsap.com/why-license/" }, + "node_modules/gtoken": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "license": "MIT", + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/gtoken/node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/gtoken/node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, "node_modules/har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -15067,7 +15273,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "dev": true, "license": "MIT", "dependencies": { "@tootallnate/once": "2", @@ -16429,6 +16634,15 @@ "node": ">=0.8.0" } }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -18831,7 +19045,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -20145,6 +20358,18 @@ "dev": true, "license": "MIT" }, + "node_modules/proto3-json-serializer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", + "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", + "license": "Apache-2.0", + "dependencies": { + "protobufjs": "^7.2.5" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/protobufjs": { "version": "7.4.0", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", @@ -20739,7 +20964,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, "license": "MIT", "dependencies": { "inherits": "^2.0.3", @@ -20960,6 +21184,20 @@ "node": ">= 4" } }, + "node_modules/retry-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz", + "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==", + "license": "MIT", + "dependencies": { + "@types/request": "^2.48.8", + "extend": "^3.0.2", + "teeny-request": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -21916,11 +22154,25 @@ "duplexer": "~0.1.1" } }, + "node_modules/stream-events": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "license": "MIT", + "dependencies": { + "stubs": "^3.0.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "license": "MIT" + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" @@ -22138,6 +22390,12 @@ "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", "license": "MIT" }, + "node_modules/stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", + "license": "MIT" + }, "node_modules/style-mod": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", @@ -22518,6 +22776,22 @@ "dev": true, "license": "ISC" }, + "node_modules/teeny-request": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", + "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", + "license": "Apache-2.0", + "dependencies": { + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.9", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/temp-file": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", @@ -24755,6 +25029,7 @@ "@connectrpc/connect": "^1.4.0", "@connectrpc/connect-node": "^1.4.0", "@getinsomnia/node-libcurl": "^2.33.7", + "@google-cloud/secret-manager": "^5.6.0", "@grpc/grpc-js": "^1.12.0", "@grpc/proto-loader": "^0.7.13", "@seald-io/nedb": "^4.0.4", @@ -24770,6 +25045,7 @@ "electron-context-menu": "^3.6.1", "electron-log": "^4.4.8", "fastq": "^1.17.1", + "google-auth-library": "^9.15.0", "graphql": "^16.8.1", "graphql-ws": "^5.16.0", "grpc-reflection-js": "Kong/grpc-reflection-js#master", diff --git a/packages/insomnia/package.json b/packages/insomnia/package.json index 7924eeec10..005e804307 100644 --- a/packages/insomnia/package.json +++ b/packages/insomnia/package.json @@ -43,6 +43,7 @@ "@connectrpc/connect": "^1.4.0", "@connectrpc/connect-node": "^1.4.0", "@getinsomnia/node-libcurl": "^2.33.7", + "@google-cloud/secret-manager": "^5.6.0", "@grpc/grpc-js": "^1.12.0", "@grpc/proto-loader": "^0.7.13", "@seald-io/nedb": "^4.0.4", @@ -58,6 +59,7 @@ "electron-context-menu": "^3.6.1", "electron-log": "^4.4.8", "fastq": "^1.17.1", + "google-auth-library": "^9.15.0", "graphql": "^16.8.1", "graphql-ws": "^5.16.0", "grpc-reflection-js": "Kong/grpc-reflection-js#master", diff --git a/packages/insomnia/src/main/ipc/cloud-service-integration/cloud-service.ts b/packages/insomnia/src/main/ipc/cloud-service-integration/cloud-service.ts index 39136ee48a..be7f6958ec 100644 --- a/packages/insomnia/src/main/ipc/cloud-service-integration/cloud-service.ts +++ b/packages/insomnia/src/main/ipc/cloud-service-integration/cloud-service.ts @@ -3,6 +3,7 @@ import * as models from '../../../models'; import type { AWSTemporaryCredential, BaseCloudCredential, CloudProviderName } from '../../../models/cloud-credential'; import { ipcMainHandle, ipcMainOn } from '../electron'; import { type AWSGetSecretConfig, AWSService } from './aws-service'; +import { type GCPGetSecretConfig, GCPService } from './gcp-servcie'; import { type MaxAgeUnit, VaultCache } from './vault-cache'; // in-memory cache for fetched vault secrets @@ -22,7 +23,7 @@ export interface CloudServiceSecretOption extends CloudServiceAuth secretId: string; config: T; } -export type CloudServiceGetSecretConfig = AWSGetSecretConfig; +export type CloudServiceGetSecretConfig = AWSGetSecretConfig | GCPGetSecretConfig; export function registerCloudServiceHandlers() { ipcMainHandle('cloudService.authenticate', (_event, options) => cloudServiceProviderAuthentication(options)); @@ -37,6 +38,8 @@ class ServiceFactory { switch (name) { case 'aws': return new AWSService(credential as AWSTemporaryCredential); + case 'gcp': + return new GCPService(credential as string); default: throw new Error('Invalid cloud service provider name'); } diff --git a/packages/insomnia/src/main/ipc/cloud-service-integration/gcp-servcie.ts b/packages/insomnia/src/main/ipc/cloud-service-integration/gcp-servcie.ts new file mode 100644 index 0000000000..48954bfb22 --- /dev/null +++ b/packages/insomnia/src/main/ipc/cloud-service-integration/gcp-servcie.ts @@ -0,0 +1,138 @@ +import { SecretManagerServiceClient } from '@google-cloud/secret-manager'; +import crypto from 'crypto'; +import { readFileSync } from 'fs'; +import { GoogleAuth, type JWTInput } from 'google-auth-library'; + +import type { CloudProviderName } from '../../../models/cloud-credential'; +import { isValidJSONString } from '../../../utils/json'; +import type { CloudServiceResult, GCPSecretConfig, ICloudService } from './types'; + +export const providerName: CloudProviderName = 'gcp'; +export type GCPGetSecretConfig = Omit; +export class GCPService implements ICloudService { + _keyPath: string; + + constructor(keyPath: string) { + this._keyPath = keyPath; + } + + _validateKeyPath(): { isValid: true; credentials: JWTInput } | { isValid: false; errorMessage: string } { + const requiredFields = ['project_id', 'private_key_id', 'private_key', 'client_email']; + const keyPath = this._keyPath; + let isValid = true; + let errorMessage = ''; + + try { + const fileContent = readFileSync(keyPath, 'utf-8'); + if (isValidJSONString(fileContent)) { + const serviceAccountKey = JSON.parse(fileContent.toString()) as JWTInput; + isValid = requiredFields.every(field => { + isValid = field in serviceAccountKey; + if (!isValid) { + errorMessage = `Required field: ${field} is missing`; + } + return isValid; + }); + if (isValid) { + return { + isValid, + credentials: serviceAccountKey, + }; + } + } else { + isValid = false; + errorMessage = `Invalid JSON in file ${keyPath}`; + }; + } catch (error) { + isValid = false; + errorMessage = error.message || error.toString(); + }; + return { isValid, errorMessage }; + } + + async authenticate(): Promise> { + const validateResult = this._validateKeyPath(); + if (validateResult.isValid) { + const auth = new GoogleAuth({ + credentials: validateResult.credentials, + // General scope for GCP + scopes: ['https://www.googleapis.com/auth/cloud-platform'], + }); + try { + const client = await auth.getClient(); + // use get access token to validate credential + await client.getAccessToken(); + return { + success: true, + result: {}, + }; + } catch (error) { + return { + success: false, + result: null, + error: { errorMessage: error?.message, errorCode: error?.code }, + }; + } + } else { + return { + success: false, + result: null, + error: { errorMessage: validateResult.errorMessage, errorCode: '' }, + }; + } + } + + getUniqueCacheKey(secretName: string, config?: GCPGetSecretConfig) { + const keyPath = this._keyPath; + const { version = 'latest' } = config || {}; + const uniqueKey = `${providerName}:${keyPath}:${secretName}:${version}`; + const uniqueKeyHash = crypto.createHash('md5').update(uniqueKey).digest('hex'); + return uniqueKeyHash; + } + + async getSecret(secretName: string, config: GCPGetSecretConfig): Promise> { + const { version } = config; + const secretVersion = version || 'latest'; + const validateResult = this._validateKeyPath(); + if (validateResult.isValid) { + const { credentials } = validateResult; + const { project_id } = credentials; + const secretClient = new SecretManagerServiceClient({ + credentials, + }); + const fullPathSecretNamePattern = /^projects\/[a-z0-9-]+\/secrets\/[a-zA-Z0-9_-]+$/; + const fullPathSecretNameWithVersionPattern = /^projects\/[a-z0-9-]+\/secrets\/[a-zA-Z0-9_-]+\/versions\/[a-zA-Z0-9_-]+$/; + let finalSecretName: string; + if (fullPathSecretNamePattern.test(secretName)) { + // if secret name in pattern /projects//secrets/ which is copied from gcp + finalSecretName = `${secretName}/versions/${secretVersion}`; + } else if (fullPathSecretNameWithVersionPattern.test(secretName)) { + // if secret name with version in pattern /projects//secrets//versions/ which is copied from gcp + finalSecretName = secretName; + } else { + finalSecretName = `projects/${project_id}/secrets/${secretName}/versions/${secretVersion}`; + } + try { + const [versionResponse] = await secretClient.accessSecretVersion({ name: finalSecretName }); + const secretResult = versionResponse.payload?.data?.toString() || ''; + return { + success: true, + result: { value: secretResult }, + }; + } catch (error) { + console.error(error); + return { + success: false, + result: null, + error: { errorMessage: error.toString(), errorCode: error?.code }, + }; + } + } else { + return { + success: false, + result: null, + error: { errorMessage: validateResult.errorMessage, errorCode: '' }, + }; + } + } +}; diff --git a/packages/insomnia/src/main/ipc/cloud-service-integration/types.ts b/packages/insomnia/src/main/ipc/cloud-service-integration/types.ts index 52cfe99506..4f6dd3f190 100644 --- a/packages/insomnia/src/main/ipc/cloud-service-integration/types.ts +++ b/packages/insomnia/src/main/ipc/cloud-service-integration/types.ts @@ -22,4 +22,9 @@ export interface AWSSecretConfig { SecretKey?: string; }; -export type ExternalVaultConfig = AWSSecretConfig; +export interface GCPSecretConfig { + secretName: string; + version?: string; +} + +export type ExternalVaultConfig = AWSSecretConfig | GCPSecretConfig; diff --git a/packages/insomnia/src/models/cloud-credential.ts b/packages/insomnia/src/models/cloud-credential.ts index bfa6f10171..109aaa3066 100644 --- a/packages/insomnia/src/models/cloud-credential.ts +++ b/packages/insomnia/src/models/cloud-credential.ts @@ -12,7 +12,7 @@ export interface AWSTemporaryCredential { sessionToken: string; region: string; } -interface IBaseCloudCredential { +export interface IBaseCloudCredential { name: string; provider: CloudProviderName; } @@ -21,7 +21,18 @@ export interface AWSCloudCredential extends IBaseCloudCredential { provider: 'aws'; credentials: AWSTemporaryCredential; } -export type BaseCloudCredential = AWSCloudCredential; +export type CloudeProviderCredentialType = AWSTemporaryCredential; +export interface AWSCloudCredential extends IBaseCloudCredential { + name: string; + provider: 'aws'; + credentials: AWSTemporaryCredential; +} +export interface GCPCloudCredential extends IBaseCloudCredential { + name: string; + provider: 'gcp'; + credentials: string; +} +export type BaseCloudCredential = AWSCloudCredential | GCPCloudCredential; export type CloudProviderCredential = BaseModel & BaseCloudCredential; export const name = 'Cloud Credential'; diff --git a/packages/insomnia/src/ui/components/modals/cloud-credential-modal/cloud-credential-modal.tsx b/packages/insomnia/src/ui/components/modals/cloud-credential-modal/cloud-credential-modal.tsx index 81a4d8e13e..61ab2234cd 100644 --- a/packages/insomnia/src/ui/components/modals/cloud-credential-modal/cloud-credential-modal.tsx +++ b/packages/insomnia/src/ui/components/modals/cloud-credential-modal/cloud-credential-modal.tsx @@ -5,6 +5,7 @@ import { useFetcher } from 'react-router-dom'; import { type BaseCloudCredential, type CloudProviderCredential, type CloudProviderName, getProviderDisplayName } from '../../../../models/cloud-credential'; import { Icon } from '../../icon'; import { AWSCredentialForm } from './aws-credential-form'; +import { GCPCredentialForm } from './gcp-credential-form'; export interface CloudCredentialModalProps { provider: CloudProviderName; @@ -87,6 +88,14 @@ export const CloudCredentialModal = (props: CloudCredentialModalProps) => { errorMessage={fetchErrorMessage} /> } + {provider === 'gcp' && + + } )} diff --git a/packages/insomnia/src/ui/components/modals/cloud-credential-modal/gcp-credential-form.tsx b/packages/insomnia/src/ui/components/modals/cloud-credential-modal/gcp-credential-form.tsx new file mode 100644 index 0000000000..ed47c263f5 --- /dev/null +++ b/packages/insomnia/src/ui/components/modals/cloud-credential-modal/gcp-credential-form.tsx @@ -0,0 +1,111 @@ +import React, { useState } from 'react'; +import { Button, Input, Label, TextField } from 'react-aria-components'; + +import { type BaseCloudCredential, type CloudProviderCredential, type CloudProviderName } from '../../../../models/cloud-credential'; +import { HelpTooltip } from '../../help-tooltip'; +import { Icon } from '../../icon'; + +export interface GCPCredentialFormProps { + data?: CloudProviderCredential; + onSubmit: (newData: BaseCloudCredential) => void; + isLoading: boolean; + errorMessage?: string; +} +const initialFormValue = { + name: '', +}; +export const providerType: CloudProviderName = 'gcp'; + +export const GCPCredentialForm = (props: GCPCredentialFormProps) => { + const { data, onSubmit, isLoading, errorMessage } = props; + const [inputKeyPath, setInputKeyPath] = useState(data?.credentials as string); + const isEdit = !!data; + const { name } = data || initialFormValue; + + const handleSelectFile = async () => { + const { canceled, filePaths } = await window.dialog.showOpenDialog({ + title: 'Select Service Account Key File', + buttonLabel: 'Select', + properties: ['openFile'], + filters: [ + { name: 'JSON File', extensions: ['json'] }, + ], + }); + if (canceled) { + return; + } + const selectedFile = filePaths[0]; + setInputKeyPath(selectedFile); + }; + + return ( +
{ + e.preventDefault(); + e.stopPropagation(); + const formData = new FormData(e.currentTarget); + const { name } = Object.fromEntries(formData.entries()) as Record; + const newData = { + name, + provider: providerType, + credentials: inputKeyPath!, + }; + onSubmit(newData); + }} + > +
+ + + + +
+ +
+
+ setInputKeyPath(e.target.value)} + /> + +
+ {(errorMessage) && +

{errorMessage}

+ } +
+ +
+
+ + ); +}; diff --git a/packages/insomnia/src/ui/components/settings/cloud-service-credentials.tsx b/packages/insomnia/src/ui/components/settings/cloud-service-credentials.tsx index b2af2ad7ac..60ab7e9d79 100644 --- a/packages/insomnia/src/ui/components/settings/cloud-service-credentials.tsx +++ b/packages/insomnia/src/ui/components/settings/cloud-service-credentials.tsx @@ -9,7 +9,9 @@ import { Icon } from '../icon'; import { showModal } from '../modals'; import { AskModal } from '../modals/ask-modal'; import { CloudCredentialModal } from '../modals/cloud-credential-modal/cloud-credential-modal'; +import { SvgIcon } from '../svg-icon'; import { UpgradeNotice } from '../upgrade-notice'; +import { NumberSetting } from './number-setting'; interface createCredentialItemType { name: string; @@ -22,6 +24,11 @@ const createCredentialItemList: createCredentialItemType[] = [ name: getProviderDisplayName('aws'), icon: , }, + { + id: 'gcp', + name: getProviderDisplayName('gcp'), + icon: , + }, ]; const buttonClassName = 'disabled:opacity-50 h-7 aspect-square aria-pressed:bg-[--hl-sm] rounded-sm text-[--color-font] hover:bg-[--hl-xs] transition-all text-sm py-1 px-2'; @@ -137,12 +144,14 @@ export const CloudServiceCredentialList = () => {
- + {(provider === 'aws' || provider === 'gcp') && + + } +
+ {modalState && modalState.show && { + const { secretName, version } = secretConfig; + if (!secretName) { + throw new Error('Get secret from GCP failed: Secret Name is required'); + } + const getSecretOption: CloudServiceSecretOption = { + provider: 'gcp', + secretId: secretName, + credentials: providerCredential.credentials, + config: { version }, + }; + const secretResult = await window.main.cloudService.getSecret(getSecretOption); + const { success, error, result } = secretResult; + if (success && result) { + return result.value; + } else { + throw new Error(`Get secret from GCP failed: ${error?.errorMessage}`); + } +}; diff --git a/packages/insomnia/src/ui/components/templating/external-vault/external-vault-form.tsx b/packages/insomnia/src/ui/components/templating/external-vault/external-vault-form.tsx index 1d836310a9..4e69cb0c45 100644 --- a/packages/insomnia/src/ui/components/templating/external-vault/external-vault-form.tsx +++ b/packages/insomnia/src/ui/components/templating/external-vault/external-vault-form.tsx @@ -2,12 +2,13 @@ import React, { useState } from 'react'; import { Button } from 'react-aria-components'; import { debounce } from '../../../../common/misc'; -import type { AWSSecretConfig, ExternalVaultConfig } from '../../../../main/ipc/cloud-service-integration/types'; +import type { AWSSecretConfig, ExternalVaultConfig, GCPSecretConfig } from '../../../../main/ipc/cloud-service-integration/types'; import { type CloudProviderCredential, type CloudProviderName, type } from '../../../../models/cloud-credential'; import { Icon } from '../../icon'; import { CloudCredentialModal } from '../../modals/cloud-credential-modal/cloud-credential-modal'; import type { ArgConfigFormProps } from '../tag-editor-arg-sub-form'; import { AWSSecretManagerForm } from './aws-secret-manager-form'; +import { GCPSecretManagerForm } from './gcp-secret-manager-form'; export const ExternalVaultForm = (props: ArgConfigFormProps) => { const { onChange, configValue, activeTagData, docs } = props; @@ -34,6 +35,15 @@ export const ExternalVaultForm = (props: ArgConfigFormProps) => { /> ); break; + case 'gcp': + SubForm = ( + + ); + break; default: SubForm = null; }; diff --git a/packages/insomnia/src/ui/components/templating/external-vault/gcp-secret-manager-form.tsx b/packages/insomnia/src/ui/components/templating/external-vault/gcp-secret-manager-form.tsx new file mode 100644 index 0000000000..d593e858b9 --- /dev/null +++ b/packages/insomnia/src/ui/components/templating/external-vault/gcp-secret-manager-form.tsx @@ -0,0 +1,58 @@ +import React from 'react'; + +import type { GCPSecretConfig } from '../../../../main/ipc/cloud-service-integration/types'; +import type { NunjucksParsedTag } from '../../../../templating/utils'; +import { HelpTooltip } from '../../help-tooltip'; + +export interface GCPSecretManagerFormProps { + formData: GCPSecretConfig; + onChange: (newConfig: GCPSecretConfig) => void; + activeTagData: NunjucksParsedTag; +} + +export const GCPSecretManagerForm = (props: GCPSecretManagerFormProps) => { + const { formData, onChange } = props; + const { + secretName, + version = 'latest', + } = formData; + const handleOnChange = (name: keyof GCPSecretConfig, newValue: string) => { + const newConfig = { + ...formData, + [name]: newValue, + }; + onChange(newConfig as unknown as GCPSecretConfig); + + }; + return ( +
+
+
+ +
+
+
+
+ +
+
+
+ ); +}; diff --git a/packages/insomnia/src/ui/components/templating/local-template-tags.ts b/packages/insomnia/src/ui/components/templating/local-template-tags.ts index 3774fda878..ef1bc2a668 100644 --- a/packages/insomnia/src/ui/components/templating/local-template-tags.ts +++ b/packages/insomnia/src/ui/components/templating/local-template-tags.ts @@ -35,6 +35,7 @@ const localTemplatePlugins: { templateTag: PluginTemplateTag }[] = [ type: 'enum', options: [ { displayName: 'AWS Secrets Manager', value: 'aws' }, + { displayName: 'GCP Secret Manager', value: 'gcp' }, ], }, { diff --git a/packages/insomnia/src/ui/components/templating/tag-editor-arg-sub-form.tsx b/packages/insomnia/src/ui/components/templating/tag-editor-arg-sub-form.tsx index 83c917c820..79372004cc 100644 --- a/packages/insomnia/src/ui/components/templating/tag-editor-arg-sub-form.tsx +++ b/packages/insomnia/src/ui/components/templating/tag-editor-arg-sub-form.tsx @@ -2,6 +2,7 @@ import React from 'react'; import type { BaseModel } from '../../../models'; import type { NunjucksParsedTag } from '../../../templating/utils'; +import { isValidJSONString } from '../../../utils/json'; import { ExternalVaultForm } from './external-vault/external-vault-form'; export interface ArgConfigFormProps { @@ -14,14 +15,13 @@ export interface ArgConfigFormProps { const formTagNameMapping = { 'vault': ExternalVaultForm, }; -const isValidJSONString = (input: string) => { - try { - const parsedJson = JSON.parse(input); - // Check if the parsed JSON is an object and not an array or null - return typeof parsedJson === 'object' && parsedJson !== null && !Array.isArray(parsedJson); - } catch (error) { - return false; - } +const isValidJSONObjectString = (input: string) => { + if (isValidJSONString(input)) { + const parsedContent = JSON.parse(input); + // Check if the parsed JSON is an real object. + return typeof parsedContent === 'object' && parsedContent !== null && !Array.isArray(parsedContent); + }; + return false; }; export const couldRenderForm = (name: string) => name in formTagNameMapping; @@ -30,7 +30,7 @@ export const ArgConfigSubForm = (props: ArgConfigFormProps) => { const tagName = activeTagDefinition.name as keyof typeof formTagNameMapping; const ConfigForm = formTagNameMapping[tagName]; - if (ConfigForm && isValidJSONString(configValue)) { + if (ConfigForm && isValidJSONObjectString(configValue)) { return ; } return configValue; diff --git a/packages/insomnia/src/utils/json.ts b/packages/insomnia/src/utils/json.ts new file mode 100644 index 0000000000..2b63146c8f --- /dev/null +++ b/packages/insomnia/src/utils/json.ts @@ -0,0 +1,8 @@ +export const isValidJSONString = (input: string): boolean => { + try { + JSON.parse(input); + return true; + } catch (error) { + return false; + } +};