mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-27 14:44:07 +00:00
feat: add node-vault util functions (#6796)
* feat: add `node-vault` util functions * fix: review comment fixes
This commit is contained in:
434
package-lock.json
generated
434
package-lock.json
generated
@@ -6458,56 +6458,6 @@
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
}
|
||||
},
|
||||
"node_modules/@postman/form-data": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@postman/form-data/-/form-data-3.1.1.tgz",
|
||||
"integrity": "sha512-vjh8Q2a8S6UCm/KKs31XFJqEEgmbjBmpPNVV2eVav6905wyFAwaUOBGA1NPBI4ERH9MMZc6w0umFgM6WbEPMdg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/@postman/tough-cookie": {
|
||||
"version": "4.1.3-postman.1",
|
||||
"resolved": "https://registry.npmjs.org/@postman/tough-cookie/-/tough-cookie-4.1.3-postman.1.tgz",
|
||||
"integrity": "sha512-txpgUqZOnWYnUHZpHjkfb0IwVH4qJmyq77pPnJLlfhMtdCLMFTEeQHlzQiK906aaNCe4NEB5fGJHo9uzGbFMeA==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"psl": "^1.1.33",
|
||||
"punycode": "^2.1.1",
|
||||
"universalify": "^0.2.0",
|
||||
"url-parse": "^1.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@postman/tough-cookie/node_modules/universalify": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
|
||||
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@postman/tunnel-agent": {
|
||||
"version": "0.6.4",
|
||||
"resolved": "https://registry.npmjs.org/@postman/tunnel-agent/-/tunnel-agent-0.6.4.tgz",
|
||||
"integrity": "sha512-CJJlq8V7rNKhAw4sBfjixKpJW00SHqebqNUQKxMoepgeWZIbdPcD+rguRcivGhS4N12PymDcKgUgSD4rVC+RjQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@prantlf/jsonlint": {
|
||||
"version": "16.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@prantlf/jsonlint/-/jsonlint-16.0.0.tgz",
|
||||
@@ -11429,15 +11379,6 @@
|
||||
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/asn1": {
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
||||
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": "~2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/asn1.js": {
|
||||
"version": "4.10.1",
|
||||
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz",
|
||||
@@ -11476,6 +11417,7 @@
|
||||
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
|
||||
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
@@ -11626,15 +11568,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-sign2": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
|
||||
"integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/aws4": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz",
|
||||
@@ -12028,15 +11961,6 @@
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bcrypt-pbkdf": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||
"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"tweetnacl": "^0.14.3"
|
||||
}
|
||||
},
|
||||
"node_modules/big.js": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
|
||||
@@ -12229,15 +12153,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/brotli": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz",
|
||||
"integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"base64-js": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/browserify-aes": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
|
||||
@@ -12750,12 +12665,6 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/caseless": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||
"integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/chai": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz",
|
||||
@@ -14375,18 +14284,6 @@
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dashdash": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
||||
"integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"assert-plus": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/data-urls": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz",
|
||||
@@ -15110,22 +15007,6 @@
|
||||
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ecc-jsbn": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
|
||||
"integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jsbn": "~0.1.0",
|
||||
"safer-buffer": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ecc-jsbn/node_modules/jsbn": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
|
||||
"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ecdsa-sig-formatter": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
||||
@@ -16424,12 +16305,6 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"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-files": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/extract-files/-/extract-files-9.0.0.tgz",
|
||||
@@ -16555,6 +16430,7 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
||||
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-levenshtein": {
|
||||
@@ -17037,15 +16913,6 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/forever-agent": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
|
||||
"integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/fork-ts-checker-webpack-plugin": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.1.0.tgz",
|
||||
@@ -17524,15 +17391,6 @@
|
||||
"node": ">=6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/getpass": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
|
||||
"integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"assert-plus": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/github-markdown-css": {
|
||||
"version": "5.8.1",
|
||||
"resolved": "https://registry.npmjs.org/github-markdown-css/-/github-markdown-css-5.8.1.tgz",
|
||||
@@ -17815,57 +17673,12 @@
|
||||
"@grpc/grpc-js": "^1.12.6"
|
||||
}
|
||||
},
|
||||
"node_modules/har-schema": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
|
||||
"integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/har-validator": {
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
|
||||
"integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
|
||||
"deprecated": "this library is no longer supported",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "^6.12.3",
|
||||
"har-schema": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/har-validator-compiled": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/har-validator-compiled/-/har-validator-compiled-1.0.0.tgz",
|
||||
"integrity": "sha512-dher7nFSx+Ef6OoqVveLClh8itAR3vd8Qx70Lh/hEgP1iGeARAolbci7Y8JBrHIYgFCT6xRdvvL16AR9Zh07Dw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/har-validator/node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/har-validator/node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@@ -18338,20 +18151,6 @@
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/http-signature": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz",
|
||||
"integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"assert-plus": "^1.0.0",
|
||||
"jsprim": "^2.0.2",
|
||||
"sshpk": "^1.14.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/http2-wrapper": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
|
||||
@@ -19168,12 +18967,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-typedarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
|
||||
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-valid-path": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-valid-path/-/is-valid-path-0.1.1.tgz",
|
||||
@@ -19236,12 +19029,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/isstream": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||
"integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/istanbul-lib-coverage": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
|
||||
@@ -20822,12 +20609,6 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/json-schema": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
|
||||
"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
|
||||
"license": "(AFL-2.1 OR BSD-3-Clause)"
|
||||
},
|
||||
"node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
@@ -20851,7 +20632,9 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
||||
"integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
|
||||
"license": "ISC"
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/json5": {
|
||||
"version": "2.2.3",
|
||||
@@ -20892,44 +20675,6 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/jsprim": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz",
|
||||
"integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==",
|
||||
"engines": [
|
||||
"node >=0.6.0"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"assert-plus": "1.0.0",
|
||||
"extsprintf": "1.3.0",
|
||||
"json-schema": "0.4.0",
|
||||
"verror": "1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jsprim/node_modules/extsprintf": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
|
||||
"integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
|
||||
"engines": [
|
||||
"node >=0.6.0"
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/jsprim/node_modules/verror": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
|
||||
"integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
|
||||
"engines": [
|
||||
"node >=0.6.0"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"assert-plus": "^1.0.0",
|
||||
"core-util-is": "1.0.2",
|
||||
"extsprintf": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jszip": {
|
||||
"version": "3.10.1",
|
||||
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
|
||||
@@ -22030,15 +21775,6 @@
|
||||
"node": ">= 6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mustache": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
|
||||
"integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"mustache": "bin/mustache"
|
||||
}
|
||||
},
|
||||
"node_modules/mz": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
|
||||
@@ -22259,44 +21995,6 @@
|
||||
"integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-vault": {
|
||||
"version": "0.10.2",
|
||||
"resolved": "https://registry.npmjs.org/node-vault/-/node-vault-0.10.2.tgz",
|
||||
"integrity": "sha512-//uc9/YImE7Dx0QHdwMiAzLaOumiKUnOUP8DymgtkZ8nsq6/V2LKvEu6kw91Lcruw8lWUfj4DO7CIXNPRWBuuA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4",
|
||||
"mustache": "^4.2.0",
|
||||
"postman-request": "^2.88.1-postman.33",
|
||||
"tv4": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-vault/node_modules/debug": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-vault/node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
@@ -22367,15 +22065,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/oauth-sign": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
|
||||
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
@@ -23090,12 +22779,6 @@
|
||||
"integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/performance-now": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
@@ -24075,57 +23758,6 @@
|
||||
"node": ">=15.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postman-request": {
|
||||
"version": "2.88.1-postman.40",
|
||||
"resolved": "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.40.tgz",
|
||||
"integrity": "sha512-uE4AiIqhjtHKp4pj9ei7fkdfNXEX9IqDBlK1plGAQne6y79UUlrTdtYLhwXoO0AMOvqyl9Ar+BU6Eo6P/MPgfg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@postman/form-data": "~3.1.1",
|
||||
"@postman/tough-cookie": "~4.1.3-postman.1",
|
||||
"@postman/tunnel-agent": "^0.6.4",
|
||||
"aws-sign2": "~0.7.0",
|
||||
"aws4": "^1.12.0",
|
||||
"brotli": "^1.3.3",
|
||||
"caseless": "~0.12.0",
|
||||
"combined-stream": "~1.0.6",
|
||||
"extend": "~3.0.2",
|
||||
"forever-agent": "~0.6.1",
|
||||
"har-validator": "~5.1.3",
|
||||
"http-signature": "~1.3.1",
|
||||
"is-typedarray": "~1.0.0",
|
||||
"isstream": "~0.1.2",
|
||||
"json-stringify-safe": "~5.0.1",
|
||||
"mime-types": "^2.1.35",
|
||||
"oauth-sign": "~0.9.0",
|
||||
"performance-now": "^2.1.0",
|
||||
"qs": "~6.5.3",
|
||||
"safe-buffer": "^5.1.2",
|
||||
"stream-length": "^1.0.2",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
}
|
||||
},
|
||||
"node_modules/postman-request/node_modules/qs": {
|
||||
"version": "6.5.3",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
|
||||
"integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/postman-request/node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||
@@ -24427,6 +24059,7 @@
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz",
|
||||
"integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"punycode": "^2.3.1"
|
||||
@@ -24471,6 +24104,7 @@
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
@@ -27386,37 +27020,6 @@
|
||||
"integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/sshpk": {
|
||||
"version": "1.18.0",
|
||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz",
|
||||
"integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asn1": "~0.2.3",
|
||||
"assert-plus": "^1.0.0",
|
||||
"bcrypt-pbkdf": "^1.0.0",
|
||||
"dashdash": "^1.12.0",
|
||||
"ecc-jsbn": "~0.1.1",
|
||||
"getpass": "^0.1.1",
|
||||
"jsbn": "~0.1.0",
|
||||
"safer-buffer": "^2.0.2",
|
||||
"tweetnacl": "~0.14.0"
|
||||
},
|
||||
"bin": {
|
||||
"sshpk-conv": "bin/sshpk-conv",
|
||||
"sshpk-sign": "bin/sshpk-sign",
|
||||
"sshpk-verify": "bin/sshpk-verify"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sshpk/node_modules/jsbn": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
|
||||
"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/stable": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
|
||||
@@ -27587,21 +27190,6 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/stream-length": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/stream-length/-/stream-length-1.0.2.tgz",
|
||||
"integrity": "sha512-aI+qKFiwoDV4rsXiS7WRoCt+v2RX1nUj17+KJC5r2gfh5xoSJIfP6Y3Do/HtvesFcTSWthIuJ3l1cvKQY/+nZg==",
|
||||
"license": "WTFPL",
|
||||
"dependencies": {
|
||||
"bluebird": "^2.6.2"
|
||||
}
|
||||
},
|
||||
"node_modules/stream-length/node_modules/bluebird": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz",
|
||||
"integrity": "sha512-UfFSr22dmHPQqPP9XWHRhq+gWnHCYguQGkXQlbyPtW5qTnhFWA8/iXg765tH0cAjy7l/zPJ1aBTO0g5XgA7kvQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/streamsearch": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
|
||||
@@ -29184,12 +28772,6 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
|
||||
"license": "Unlicense"
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||
@@ -29454,6 +29036,7 @@
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
|
||||
"devOptional": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"punycode": "^2.1.0"
|
||||
@@ -35492,7 +35075,6 @@
|
||||
"moment": "^2.29.4",
|
||||
"nanoid": "3.3.8",
|
||||
"node-fetch": "^2.7.0",
|
||||
"node-vault": "^0.10.2",
|
||||
"path": "^0.12.7",
|
||||
"quickjs-emscripten": "^0.29.2",
|
||||
"tv4": "^1.3.0",
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
"moment": "^2.29.4",
|
||||
"nanoid": "3.3.8",
|
||||
"node-fetch": "^2.7.0",
|
||||
"node-vault": "^0.10.2",
|
||||
"path": "^0.12.7",
|
||||
"quickjs-emscripten": "^0.29.2",
|
||||
"tv4": "^1.3.0",
|
||||
|
||||
@@ -4,5 +4,7 @@ export { WsClient } from './ws/ws-client';
|
||||
export { default as cookies } from './cookies';
|
||||
|
||||
export { getCACertificates } from './utils/ca-cert';
|
||||
export { default as createVaultClient, VaultError } from './utils/node-vault';
|
||||
export type { VaultClient, VaultConfig, VaultRequestOptions } from './utils/node-vault';
|
||||
|
||||
export * as scripting from './scripting';
|
||||
|
||||
716
packages/bruno-requests/src/utils/node-vault.spec.ts
Normal file
716
packages/bruno-requests/src/utils/node-vault.spec.ts
Normal file
@@ -0,0 +1,716 @@
|
||||
import axios from 'axios';
|
||||
import createVaultClient, { VaultError, VaultClient } from './node-vault';
|
||||
|
||||
// Mock axios
|
||||
jest.mock('axios', () => {
|
||||
const mockAxios = jest.fn();
|
||||
(mockAxios as any).isAxiosError = jest.fn((error: any) => error.isAxiosError === true);
|
||||
return mockAxios;
|
||||
});
|
||||
|
||||
const mockedAxios = axios as jest.MockedFunction<typeof axios>;
|
||||
|
||||
describe('node-vault', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
// Clear environment variables
|
||||
delete process.env.VAULT_ADDR;
|
||||
delete process.env.VAULT_TOKEN;
|
||||
delete process.env.VAULT_NAMESPACE;
|
||||
});
|
||||
|
||||
describe('module', () => {
|
||||
it('should export a function that returns a new client', () => {
|
||||
const vault = createVaultClient();
|
||||
expect(typeof createVaultClient).toBe('function');
|
||||
expect(typeof vault).toBe('object');
|
||||
});
|
||||
|
||||
it('should set default values for endpoint and apiVersion', () => {
|
||||
const vault = createVaultClient();
|
||||
expect(vault.endpoint).toBe('http://127.0.0.1:8200');
|
||||
expect(vault.apiVersion).toBe('v1');
|
||||
});
|
||||
|
||||
it('should use environment variables for defaults', () => {
|
||||
process.env.VAULT_ADDR = 'https://vault.example.com';
|
||||
process.env.VAULT_TOKEN = 'env-token';
|
||||
process.env.VAULT_NAMESPACE = 'env-namespace';
|
||||
|
||||
const vault = createVaultClient();
|
||||
expect(vault.endpoint).toBe('https://vault.example.com');
|
||||
expect(vault.token).toBe('env-token');
|
||||
expect(vault.namespace).toBe('env-namespace');
|
||||
});
|
||||
|
||||
it('should allow config to override environment variables', () => {
|
||||
process.env.VAULT_ADDR = 'https://vault.example.com';
|
||||
process.env.VAULT_TOKEN = 'env-token';
|
||||
|
||||
const vault = createVaultClient({
|
||||
endpoint: 'https://custom.vault.com',
|
||||
token: 'config-token'
|
||||
});
|
||||
expect(vault.endpoint).toBe('https://custom.vault.com');
|
||||
expect(vault.token).toBe('config-token');
|
||||
});
|
||||
});
|
||||
|
||||
describe('client properties', () => {
|
||||
it('should allow direct assignment of endpoint', () => {
|
||||
const vault = createVaultClient();
|
||||
vault.endpoint = 'https://new-vault.example.com';
|
||||
expect(vault.endpoint).toBe('https://new-vault.example.com');
|
||||
});
|
||||
|
||||
it('should allow direct assignment of token', () => {
|
||||
const vault = createVaultClient();
|
||||
vault.token = 'new-token';
|
||||
expect(vault.token).toBe('new-token');
|
||||
});
|
||||
|
||||
it('should allow direct assignment of namespace', () => {
|
||||
const vault = createVaultClient();
|
||||
vault.namespace = 'my-namespace';
|
||||
expect(vault.namespace).toBe('my-namespace');
|
||||
});
|
||||
|
||||
it('should allow direct assignment of apiVersion', () => {
|
||||
const vault = createVaultClient();
|
||||
vault.apiVersion = 'v2';
|
||||
expect(vault.apiVersion).toBe('v2');
|
||||
});
|
||||
});
|
||||
|
||||
describe('read(path, requestOptions)', () => {
|
||||
let vault: VaultClient;
|
||||
|
||||
beforeEach(() => {
|
||||
vault = createVaultClient({
|
||||
endpoint: 'http://localhost:8200',
|
||||
token: 'test-token'
|
||||
});
|
||||
});
|
||||
|
||||
it('should read data from path', async () => {
|
||||
const responseData = { data: { value: 'secret-value' } };
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 200,
|
||||
data: responseData
|
||||
});
|
||||
|
||||
const result = await vault.read('secret/data/hello');
|
||||
|
||||
expect(mockedAxios).toHaveBeenCalledTimes(1);
|
||||
expect(mockedAxios).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: 'GET',
|
||||
url: 'http://localhost:8200/v1/secret/data/hello',
|
||||
headers: expect.objectContaining({
|
||||
'X-Vault-Token': 'test-token'
|
||||
})
|
||||
})
|
||||
);
|
||||
expect(result).toEqual(responseData);
|
||||
});
|
||||
|
||||
it('should include namespace header when set', async () => {
|
||||
vault.namespace = 'my-namespace';
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 200,
|
||||
data: { data: {} }
|
||||
});
|
||||
|
||||
await vault.read('secret/data/hello');
|
||||
|
||||
expect(mockedAxios).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
headers: expect.objectContaining({
|
||||
'X-Vault-Token': 'test-token',
|
||||
'X-Vault-Namespace': 'my-namespace'
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should use updated endpoint after assignment', async () => {
|
||||
vault.endpoint = 'https://new-vault.com';
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 200,
|
||||
data: { data: {} }
|
||||
});
|
||||
|
||||
await vault.read('secret/data/hello');
|
||||
|
||||
expect(mockedAxios).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
url: 'https://new-vault.com/v1/secret/data/hello'
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle 404 errors', async () => {
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 404,
|
||||
data: { errors: ['no secrets found'] }
|
||||
});
|
||||
|
||||
await expect(vault.read('secret/data/nonexistent')).rejects.toThrow('no secrets found');
|
||||
});
|
||||
|
||||
it('should handle 204 no content response', async () => {
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 204,
|
||||
data: null
|
||||
});
|
||||
|
||||
const result = await vault.read('secret/data/empty');
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should handle paths with leading slash without creating double slashes', async () => {
|
||||
const responseData = { data: { value: 'secret-value' } };
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 200,
|
||||
data: responseData
|
||||
});
|
||||
|
||||
const result = await vault.read('/secret/data/hello');
|
||||
|
||||
expect(mockedAxios).toHaveBeenCalledTimes(1);
|
||||
expect(mockedAxios).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: 'GET',
|
||||
url: 'http://localhost:8200/v1/secret/data/hello',
|
||||
headers: expect.objectContaining({
|
||||
'X-Vault-Token': 'test-token'
|
||||
})
|
||||
})
|
||||
);
|
||||
expect(result).toEqual(responseData);
|
||||
});
|
||||
|
||||
it('should handle endpoint with trailing slash', async () => {
|
||||
vault.endpoint = 'http://localhost:8200/';
|
||||
const responseData = { data: { value: 'secret-value' } };
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 200,
|
||||
data: responseData
|
||||
});
|
||||
|
||||
const result = await vault.read('secret/data/hello');
|
||||
|
||||
expect(mockedAxios).toHaveBeenCalledTimes(1);
|
||||
expect(mockedAxios).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: 'GET',
|
||||
url: 'http://localhost:8200/v1/secret/data/hello'
|
||||
})
|
||||
);
|
||||
expect(result).toEqual(responseData);
|
||||
});
|
||||
});
|
||||
|
||||
describe('write(path, data, requestOptions)', () => {
|
||||
let vault: VaultClient;
|
||||
|
||||
beforeEach(() => {
|
||||
vault = createVaultClient({
|
||||
endpoint: 'http://localhost:8200',
|
||||
token: 'test-token'
|
||||
});
|
||||
});
|
||||
|
||||
it('should write data to path', async () => {
|
||||
const writeData = { value: 'world' };
|
||||
const responseData = { data: { created_time: '2024-01-01T00:00:00Z' } };
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 200,
|
||||
data: responseData
|
||||
});
|
||||
|
||||
const result = await vault.write('secret/data/hello', writeData);
|
||||
|
||||
expect(mockedAxios).toHaveBeenCalledTimes(1);
|
||||
expect(mockedAxios).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: 'POST',
|
||||
url: 'http://localhost:8200/v1/secret/data/hello',
|
||||
data: writeData,
|
||||
headers: expect.objectContaining({
|
||||
'X-Vault-Token': 'test-token',
|
||||
'Content-Type': 'application/json'
|
||||
})
|
||||
})
|
||||
);
|
||||
expect(result).toEqual(responseData);
|
||||
});
|
||||
|
||||
it('should handle LDAP login write', async () => {
|
||||
const loginData = { password: 'my-password' };
|
||||
const responseData = {
|
||||
auth: {
|
||||
client_token: 'ldap-token',
|
||||
renewable: true,
|
||||
lease_duration: 3600
|
||||
}
|
||||
};
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 200,
|
||||
data: responseData
|
||||
});
|
||||
|
||||
const result = await vault.write('auth/ldap/login/myuser', loginData);
|
||||
|
||||
expect(mockedAxios).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: 'POST',
|
||||
url: 'http://localhost:8200/v1/auth/ldap/login/myuser',
|
||||
data: loginData
|
||||
})
|
||||
);
|
||||
expect(result.auth.client_token).toBe('ldap-token');
|
||||
});
|
||||
});
|
||||
|
||||
describe('approleLogin(args)', () => {
|
||||
let vault: VaultClient;
|
||||
|
||||
beforeEach(() => {
|
||||
vault = createVaultClient({
|
||||
endpoint: 'http://localhost:8200'
|
||||
});
|
||||
});
|
||||
|
||||
it('should login with role_id and secret_id', async () => {
|
||||
const responseData = {
|
||||
auth: {
|
||||
client_token: 'approle-token',
|
||||
renewable: true,
|
||||
lease_duration: 3600,
|
||||
policies: ['default', 'my-policy']
|
||||
}
|
||||
};
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 200,
|
||||
data: responseData
|
||||
});
|
||||
|
||||
const result = await vault.approleLogin({
|
||||
role_id: 'my-role-id',
|
||||
secret_id: 'my-secret-id'
|
||||
});
|
||||
|
||||
expect(mockedAxios).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: 'POST',
|
||||
url: 'http://localhost:8200/v1/auth/approle/login',
|
||||
data: {
|
||||
role_id: 'my-role-id',
|
||||
secret_id: 'my-secret-id'
|
||||
}
|
||||
})
|
||||
);
|
||||
expect(result.auth.client_token).toBe('approle-token');
|
||||
});
|
||||
|
||||
it('should login with only role_id when secret_id is not required', async () => {
|
||||
const responseData = {
|
||||
auth: { client_token: 'approle-token' }
|
||||
};
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 200,
|
||||
data: responseData
|
||||
});
|
||||
|
||||
await vault.approleLogin({
|
||||
role_id: 'my-role-id'
|
||||
});
|
||||
|
||||
expect(mockedAxios).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
data: {
|
||||
role_id: 'my-role-id'
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should use custom mount_point', async () => {
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 200,
|
||||
data: { auth: { client_token: 'token' } }
|
||||
});
|
||||
|
||||
await vault.approleLogin({
|
||||
role_id: 'my-role-id',
|
||||
secret_id: 'my-secret-id',
|
||||
mount_point: 'custom-approle'
|
||||
});
|
||||
|
||||
expect(mockedAxios).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
url: 'http://localhost:8200/v1/auth/custom-approle/login'
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle authentication errors', async () => {
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 400,
|
||||
data: { errors: ['invalid role or secret ID'] }
|
||||
});
|
||||
|
||||
await expect(vault.approleLogin({
|
||||
role_id: 'bad-role-id',
|
||||
secret_id: 'bad-secret-id'
|
||||
})).rejects.toThrow('invalid role or secret ID');
|
||||
});
|
||||
});
|
||||
|
||||
describe('tokenLookupSelf()', () => {
|
||||
let vault: VaultClient;
|
||||
|
||||
beforeEach(() => {
|
||||
vault = createVaultClient({
|
||||
endpoint: 'http://localhost:8200',
|
||||
token: 'my-token'
|
||||
});
|
||||
});
|
||||
|
||||
it('should lookup current token', async () => {
|
||||
const responseData = {
|
||||
data: {
|
||||
id: 'my-token',
|
||||
ttl: 3600,
|
||||
renewable: true,
|
||||
policies: ['default']
|
||||
}
|
||||
};
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 200,
|
||||
data: responseData
|
||||
});
|
||||
|
||||
const result = await vault.tokenLookupSelf();
|
||||
|
||||
expect(mockedAxios).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: 'GET',
|
||||
url: 'http://localhost:8200/v1/auth/token/lookup-self',
|
||||
headers: expect.objectContaining({
|
||||
'X-Vault-Token': 'my-token'
|
||||
})
|
||||
})
|
||||
);
|
||||
expect(result.data.ttl).toBe(3600);
|
||||
});
|
||||
|
||||
it('should handle expired token error', async () => {
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 403,
|
||||
data: { errors: ['permission denied'] }
|
||||
});
|
||||
|
||||
await expect(vault.tokenLookupSelf()).rejects.toThrow('permission denied');
|
||||
});
|
||||
});
|
||||
|
||||
describe('tokenRenewSelf(args)', () => {
|
||||
let vault: VaultClient;
|
||||
|
||||
beforeEach(() => {
|
||||
vault = createVaultClient({
|
||||
endpoint: 'http://localhost:8200',
|
||||
token: 'my-token'
|
||||
});
|
||||
});
|
||||
|
||||
it('should renew current token', async () => {
|
||||
const responseData = {
|
||||
auth: {
|
||||
client_token: 'my-token',
|
||||
renewable: true,
|
||||
lease_duration: 7200
|
||||
}
|
||||
};
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 200,
|
||||
data: responseData
|
||||
});
|
||||
|
||||
const result = await vault.tokenRenewSelf();
|
||||
|
||||
expect(mockedAxios).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: 'POST',
|
||||
url: 'http://localhost:8200/v1/auth/token/renew-self',
|
||||
headers: expect.objectContaining({
|
||||
'X-Vault-Token': 'my-token'
|
||||
})
|
||||
})
|
||||
);
|
||||
expect(result.auth.lease_duration).toBe(7200);
|
||||
});
|
||||
|
||||
it('should pass increment when provided', async () => {
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 200,
|
||||
data: { auth: { lease_duration: 3600 } }
|
||||
});
|
||||
|
||||
await vault.tokenRenewSelf({ increment: 3600 });
|
||||
|
||||
expect(mockedAxios).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
data: { increment: 3600 }
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle non-renewable token error', async () => {
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 400,
|
||||
data: { errors: ['lease is not renewable'] }
|
||||
});
|
||||
|
||||
await expect(vault.tokenRenewSelf()).rejects.toThrow('lease is not renewable');
|
||||
});
|
||||
});
|
||||
|
||||
describe('error handling', () => {
|
||||
let vault: VaultClient;
|
||||
|
||||
beforeEach(() => {
|
||||
vault = createVaultClient({
|
||||
endpoint: 'http://localhost:8200',
|
||||
token: 'test-token'
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw VaultError with response structure', async () => {
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 500,
|
||||
data: { errors: ['internal server error'] }
|
||||
});
|
||||
|
||||
try {
|
||||
await vault.read('secret/data/hello');
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(VaultError);
|
||||
expect((error as VaultError).message).toBe('internal server error');
|
||||
expect((error as VaultError).response).toEqual({
|
||||
statusCode: 500,
|
||||
status: 500,
|
||||
body: { errors: ['internal server error'] }
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle error without errors array', async () => {
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 503,
|
||||
data: {}
|
||||
});
|
||||
|
||||
await expect(vault.read('secret/data/hello')).rejects.toThrow('Status 503');
|
||||
});
|
||||
|
||||
it('should handle network errors', async () => {
|
||||
const networkError = new Error('Network Error');
|
||||
(networkError as any).isAxiosError = true;
|
||||
(networkError as any).code = 'ECONNREFUSED';
|
||||
mockedAxios.mockRejectedValueOnce(networkError);
|
||||
|
||||
try {
|
||||
await vault.read('secret/data/hello');
|
||||
} catch (error) {
|
||||
expect((error as any).message).toBe('Network Error');
|
||||
expect((error as any).code).toBe('ECONNREFUSED');
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle axios error with response', async () => {
|
||||
const axiosError = new Error('Request failed');
|
||||
(axiosError as any).isAxiosError = true;
|
||||
(axiosError as any).response = {
|
||||
status: 401,
|
||||
data: { errors: ['permission denied'] }
|
||||
};
|
||||
mockedAxios.mockRejectedValueOnce(axiosError);
|
||||
|
||||
await expect(vault.read('secret/data/hello')).rejects.toThrow('permission denied');
|
||||
});
|
||||
|
||||
it('should pass through non-axios errors', async () => {
|
||||
const genericError = new Error('Unknown error');
|
||||
mockedAxios.mockRejectedValueOnce(genericError);
|
||||
|
||||
await expect(vault.read('secret/data/hello')).rejects.toThrow('Unknown error');
|
||||
});
|
||||
});
|
||||
|
||||
describe('requestOptions', () => {
|
||||
it('should pass strictSSL to https agent', async () => {
|
||||
const vault = createVaultClient({
|
||||
endpoint: 'https://vault.example.com',
|
||||
token: 'test-token',
|
||||
requestOptions: {
|
||||
strictSSL: false
|
||||
}
|
||||
});
|
||||
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 200,
|
||||
data: {}
|
||||
});
|
||||
|
||||
await vault.read('secret/data/hello');
|
||||
|
||||
expect(mockedAxios).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
httpsAgent: expect.any(Object)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should not set httpsAgent for http endpoints', async () => {
|
||||
const vault = createVaultClient({
|
||||
endpoint: 'http://localhost:8200',
|
||||
token: 'test-token',
|
||||
requestOptions: {
|
||||
strictSSL: false
|
||||
}
|
||||
});
|
||||
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 200,
|
||||
data: {}
|
||||
});
|
||||
|
||||
await vault.read('secret/data/hello');
|
||||
|
||||
const callArgs = mockedAxios.mock.calls[0][0] as any;
|
||||
expect(callArgs.httpsAgent).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should configure proxy when provided', async () => {
|
||||
const vault = createVaultClient({
|
||||
endpoint: 'http://localhost:8200',
|
||||
token: 'test-token',
|
||||
requestOptions: {
|
||||
proxy: 'http://proxy.example.com:8080'
|
||||
}
|
||||
});
|
||||
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 200,
|
||||
data: {}
|
||||
});
|
||||
|
||||
await vault.read('secret/data/hello');
|
||||
|
||||
expect(mockedAxios).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
proxy: expect.objectContaining({
|
||||
host: 'proxy.example.com',
|
||||
port: 8080
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should configure proxy with authentication', async () => {
|
||||
const vault = createVaultClient({
|
||||
endpoint: 'http://localhost:8200',
|
||||
token: 'test-token',
|
||||
requestOptions: {
|
||||
proxy: 'http://user:pass@proxy.example.com:8080'
|
||||
}
|
||||
});
|
||||
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 200,
|
||||
data: {}
|
||||
});
|
||||
|
||||
await vault.read('secret/data/hello');
|
||||
|
||||
expect(mockedAxios).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
proxy: expect.objectContaining({
|
||||
host: 'proxy.example.com',
|
||||
port: 8080,
|
||||
auth: {
|
||||
username: 'user',
|
||||
password: 'pass'
|
||||
}
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('URL construction', () => {
|
||||
it('should construct URL with apiVersion', async () => {
|
||||
const vault = createVaultClient({
|
||||
endpoint: 'http://localhost:8200',
|
||||
apiVersion: 'v2'
|
||||
});
|
||||
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 200,
|
||||
data: {}
|
||||
});
|
||||
|
||||
await vault.read('secret/data/hello');
|
||||
|
||||
expect(mockedAxios).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
url: 'http://localhost:8200/v2/secret/data/hello'
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle endpoint without trailing slash', async () => {
|
||||
const vault = createVaultClient({
|
||||
endpoint: 'http://localhost:8200'
|
||||
});
|
||||
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 200,
|
||||
data: {}
|
||||
});
|
||||
|
||||
await vault.read('secret/data/hello');
|
||||
|
||||
expect(mockedAxios).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
url: 'http://localhost:8200/v1/secret/data/hello'
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('health endpoint handling', () => {
|
||||
it('should not throw error for sys/health even with non-200 status', async () => {
|
||||
const vault = createVaultClient({
|
||||
endpoint: 'http://localhost:8200'
|
||||
});
|
||||
|
||||
const healthResponse = {
|
||||
initialized: true,
|
||||
sealed: true,
|
||||
standby: true
|
||||
};
|
||||
|
||||
mockedAxios.mockResolvedValueOnce({
|
||||
status: 503,
|
||||
data: healthResponse
|
||||
});
|
||||
|
||||
const result = await vault.read('sys/health');
|
||||
expect(result).toEqual(healthResponse);
|
||||
});
|
||||
});
|
||||
});
|
||||
326
packages/bruno-requests/src/utils/node-vault.ts
Normal file
326
packages/bruno-requests/src/utils/node-vault.ts
Normal file
@@ -0,0 +1,326 @@
|
||||
import axios, { AxiosRequestConfig, AxiosError } from 'axios';
|
||||
import * as https from 'node:https';
|
||||
|
||||
/**
|
||||
* Configuration options for creating a Vault client
|
||||
*/
|
||||
export interface VaultConfig {
|
||||
apiVersion?: string;
|
||||
endpoint?: string;
|
||||
token?: string;
|
||||
namespace?: string;
|
||||
requestOptions?: VaultRequestOptions;
|
||||
debug?: (...args: any[]) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request options for Vault HTTP requests
|
||||
* Compatible with node-vault's requestOptions
|
||||
*/
|
||||
export interface VaultRequestOptions {
|
||||
strictSSL?: boolean;
|
||||
ca?: string | Buffer | Array<string | Buffer>;
|
||||
proxy?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* AppRole login arguments
|
||||
*/
|
||||
export interface ApproleLoginArgs {
|
||||
role?: string;
|
||||
role_id: string;
|
||||
secret_id?: string;
|
||||
mount_point?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Token renew arguments
|
||||
*/
|
||||
export interface TokenRenewArgs {
|
||||
increment?: number | string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vault API response error structure
|
||||
* Includes both statusCode (node-vault style) and status (axios style) for compatibility
|
||||
*/
|
||||
export class VaultError extends Error {
|
||||
response?: {
|
||||
statusCode: number;
|
||||
status: number; // Alias for axios-style error handling
|
||||
body: any;
|
||||
};
|
||||
|
||||
code?: string; // For network errors
|
||||
|
||||
constructor(message: string, response?: { statusCode: number; body: any }) {
|
||||
super(message);
|
||||
this.name = 'VaultError';
|
||||
if (response) {
|
||||
this.response = {
|
||||
statusCode: response.statusCode,
|
||||
status: response.statusCode, // Alias for compatibility
|
||||
body: response.body
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vault client interface - matches node-vault API surface
|
||||
*/
|
||||
export interface VaultClient {
|
||||
endpoint: string;
|
||||
namespace?: string;
|
||||
token?: string;
|
||||
apiVersion: string;
|
||||
|
||||
read(path: string, requestOptions?: VaultRequestOptions): Promise<any>;
|
||||
write(path: string, data: any, requestOptions?: VaultRequestOptions): Promise<any>;
|
||||
approleLogin(args: ApproleLoginArgs): Promise<any>;
|
||||
tokenLookupSelf(args?: any): Promise<any>;
|
||||
tokenRenewSelf(args?: TokenRenewArgs): Promise<any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an HTTPS agent based on request options
|
||||
*/
|
||||
function createHttpsAgent(options: VaultRequestOptions): https.Agent | undefined {
|
||||
const agentOptions: https.AgentOptions = {};
|
||||
let needsAgent = false;
|
||||
|
||||
if (options.strictSSL === false) {
|
||||
agentOptions.rejectUnauthorized = false;
|
||||
needsAgent = true;
|
||||
}
|
||||
|
||||
if (options.ca) {
|
||||
agentOptions.ca = options.ca;
|
||||
needsAgent = true;
|
||||
}
|
||||
|
||||
return needsAgent ? new https.Agent(agentOptions) : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles Vault API response, extracting body or throwing error
|
||||
*/
|
||||
function handleVaultResponse(statusCode: number, body: any, path: string): any {
|
||||
// Success responses
|
||||
if (statusCode === 200 || statusCode === 204) {
|
||||
return body;
|
||||
}
|
||||
|
||||
// Health endpoint special handling (matches node-vault behavior)
|
||||
if (path.match(/sys\/health/) !== null) {
|
||||
return body;
|
||||
}
|
||||
|
||||
// Error responses
|
||||
let message: string;
|
||||
if (body && body.errors && body.errors.length > 0) {
|
||||
message = body.errors[0];
|
||||
} else {
|
||||
message = `Status ${statusCode}`;
|
||||
}
|
||||
|
||||
throw new VaultError(message, { statusCode, body });
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Vault client instance
|
||||
*
|
||||
* This is a drop-in replacement for node-vault, implementing only the methods
|
||||
* used by bruno-electron and bruno-cli.
|
||||
*
|
||||
* @param config - Configuration options
|
||||
* @returns VaultClient instance with mutable properties
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* const vault = createVaultClient({ apiVersion: 'v1' });
|
||||
* vault.endpoint = 'https://vault.example.com';
|
||||
* vault.token = 'my-token';
|
||||
* const secret = await vault.read('secret/data/myapp');
|
||||
* ```
|
||||
*/
|
||||
function createVaultClient(config: VaultConfig = {}): VaultClient {
|
||||
const debug = config.debug || (() => {});
|
||||
const defaultRequestOptions = config.requestOptions || {};
|
||||
|
||||
/**
|
||||
* Makes an HTTP request to the Vault API
|
||||
*/
|
||||
async function request(
|
||||
method: string,
|
||||
path: string,
|
||||
data?: any,
|
||||
requestOptions?: VaultRequestOptions
|
||||
): Promise<any> {
|
||||
// Merge request options: defaults from config + per-request options
|
||||
const mergedOptions: VaultRequestOptions = {
|
||||
...defaultRequestOptions,
|
||||
...requestOptions
|
||||
};
|
||||
|
||||
const endpointOrigin = client.endpoint?.endsWith('/') ? client.endpoint : `${client.endpoint}/`;
|
||||
|
||||
// Build URL
|
||||
const uri = `${endpointOrigin}${client.apiVersion}${path}`;
|
||||
debug(method, uri);
|
||||
|
||||
// Build headers
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json'
|
||||
};
|
||||
|
||||
if (typeof client.token === 'string' && client.token.length) {
|
||||
headers['X-Vault-Token'] = client.token;
|
||||
}
|
||||
|
||||
if (typeof client.namespace === 'string' && client.namespace.length) {
|
||||
headers['X-Vault-Namespace'] = client.namespace;
|
||||
}
|
||||
|
||||
// Build axios config
|
||||
const axiosConfig: AxiosRequestConfig = {
|
||||
method: method as any,
|
||||
url: uri,
|
||||
headers,
|
||||
validateStatus: () => true // Don't throw on non-2xx status
|
||||
};
|
||||
|
||||
// Add request body for POST/PUT
|
||||
if (data && (method === 'POST' || method === 'PUT')) {
|
||||
axiosConfig.data = data;
|
||||
debug('data:', data);
|
||||
}
|
||||
|
||||
// Configure HTTPS agent
|
||||
if (uri.startsWith('https')) {
|
||||
const agent = createHttpsAgent(mergedOptions);
|
||||
if (agent) {
|
||||
axiosConfig.httpsAgent = agent;
|
||||
}
|
||||
}
|
||||
|
||||
// Configure proxy
|
||||
if (mergedOptions.proxy) {
|
||||
// Parse proxy URL into axios proxy config
|
||||
try {
|
||||
const proxyUrl = new URL(mergedOptions.proxy);
|
||||
axiosConfig.proxy = {
|
||||
host: proxyUrl.hostname,
|
||||
port: parseInt(proxyUrl.port, 10) || (proxyUrl.protocol === 'https:' ? 443 : 80),
|
||||
protocol: proxyUrl.protocol.replace(':', '')
|
||||
};
|
||||
if (proxyUrl.username && proxyUrl.password) {
|
||||
axiosConfig.proxy.auth = {
|
||||
username: decodeURIComponent(proxyUrl.username),
|
||||
password: decodeURIComponent(proxyUrl.password)
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
// If proxy URL parsing fails, pass it as-is for backward compatibility
|
||||
debug('Failed to parse proxy URL:', mergedOptions.proxy);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios(axiosConfig);
|
||||
return handleVaultResponse(response.status, response.data, path);
|
||||
} catch (error) {
|
||||
// Network errors or other axios errors
|
||||
if (axios.isAxiosError(error)) {
|
||||
const axiosError = error as AxiosError;
|
||||
if (axiosError.response) {
|
||||
// Server responded with error status
|
||||
return handleVaultResponse(
|
||||
axiosError.response.status,
|
||||
axiosError.response.data,
|
||||
path
|
||||
);
|
||||
}
|
||||
// Network error - preserve original error structure
|
||||
const vaultError = new VaultError(axiosError.message);
|
||||
(vaultError as any).code = axiosError.code;
|
||||
throw vaultError;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Create client object with mutable properties
|
||||
const client: VaultClient = {
|
||||
// Mutable properties (support direct assignment like node-vault)
|
||||
apiVersion: config.apiVersion || 'v1',
|
||||
endpoint: config.endpoint || process.env.VAULT_ADDR || 'http://127.0.0.1:8200',
|
||||
token: config.token || process.env.VAULT_TOKEN,
|
||||
namespace: config.namespace || process.env.VAULT_NAMESPACE,
|
||||
|
||||
/**
|
||||
* Read data from a Vault path
|
||||
* @param path - The path to read from (e.g., 'secret/data/myapp')
|
||||
* @param requestOptions - Optional request options
|
||||
*/
|
||||
async read(path: string, requestOptions?: VaultRequestOptions): Promise<any> {
|
||||
path = path.startsWith('/') ? path : `/${path}`;
|
||||
debug('read', path);
|
||||
return request('GET', path, undefined, requestOptions);
|
||||
},
|
||||
|
||||
/**
|
||||
* Write data to a Vault path
|
||||
* @param path - The path to write to
|
||||
* @param data - The data to write
|
||||
* @param requestOptions - Optional request options
|
||||
*/
|
||||
async write(path: string, data: any, requestOptions?: VaultRequestOptions): Promise<any> {
|
||||
path = path.startsWith('/') ? path : `/${path}`;
|
||||
debug('write', path, data);
|
||||
return request('POST', path, data, requestOptions);
|
||||
},
|
||||
|
||||
/**
|
||||
* Authenticate using AppRole
|
||||
* @param args - AppRole login arguments
|
||||
*/
|
||||
async approleLogin(args: ApproleLoginArgs): Promise<any> {
|
||||
debug('approleLogin', args.role_id);
|
||||
const mountPoint = args.mount_point || 'approle';
|
||||
const body: Record<string, any> = {
|
||||
role_id: args.role_id
|
||||
};
|
||||
if (args.secret_id) {
|
||||
body.secret_id = args.secret_id;
|
||||
}
|
||||
return request('POST', `/auth/${mountPoint}/login`, body);
|
||||
},
|
||||
|
||||
/**
|
||||
* Look up the current token's properties
|
||||
*/
|
||||
async tokenLookupSelf(args?: any): Promise<any> {
|
||||
debug('tokenLookupSelf');
|
||||
return request('GET', '/auth/token/lookup-self');
|
||||
},
|
||||
|
||||
/**
|
||||
* Renew the current token
|
||||
* @param args - Optional arguments including increment
|
||||
*/
|
||||
async tokenRenewSelf(args?: TokenRenewArgs): Promise<any> {
|
||||
debug('tokenRenewSelf');
|
||||
const body: Record<string, any> = {};
|
||||
if (args?.increment !== undefined) {
|
||||
body.increment = args.increment;
|
||||
}
|
||||
return request('POST', '/auth/token/renew-self', Object.keys(body).length > 0 ? body : undefined);
|
||||
}
|
||||
};
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
export default createVaultClient;
|
||||
Reference in New Issue
Block a user