Compare commits

..

70 Commits

Author SHA1 Message Date
c37ce4eaea add refresh token to cli 2023-05-24 18:51:42 -04:00
17647587f9 remove tests for time being 2023-05-24 10:48:11 -04:00
f3dc7fcf7b add timout to pull requests 2023-05-24 10:48:11 -04:00
e65c6568e1 Modify convention for PostHog isPaid attr to be tier-based instead of slug 2023-05-24 10:26:06 +03:00
9d40a96633 Update README.md 2023-05-23 20:22:01 -04:00
859fe09ac6 Merge pull request #598 from Infisical/maidul98-patch-1
add pre commit install command to README.md
2023-05-23 20:20:57 -04:00
d0d6419d4d add pre commit install command to README.md 2023-05-23 20:20:10 -04:00
8b05ce11f7 add pre commit to husky 2023-05-23 20:15:39 -04:00
a7fb0786f9 improve pre commit docs 2023-05-23 19:45:10 -04:00
f2de1778cb catch case when hook path is default 2023-05-23 19:34:31 -04:00
952cf47b9a Merge branch 'main' of https://github.com/Infisical/infisical 2023-05-23 15:41:43 -07:00
1d17596af1 added boolean flag for the plan in posthog logging 2023-05-23 15:41:34 -07:00
01385687e0 make posthog failed calls level=debug 2023-05-23 18:16:13 -04:00
d2e3aa15b0 patch standalone docker image 2023-05-23 17:16:32 -04:00
96607153dc Modularize getOrganizationPlan function 2023-05-23 23:54:54 +03:00
a8502377c7 Add endpoint for updating organization plan 2023-05-23 23:14:20 +03:00
5aa99001cc Merge pull request #597 from Infisical/connect-to-license-server
Added add/remove/get organization payment methods and get cloud plans…
2023-05-23 22:39:35 +03:00
83dd35299c Added add/remove/get organization payment methods and get cloud plans from license server 2023-05-23 22:28:41 +03:00
b5b2f402ad add missing required envrs 2023-05-23 14:09:45 -04:00
ec34572087 patch invite only 2023-05-23 13:18:21 -04:00
7f7d120c2f Merge pull request #595 from Infisical/connect-to-license-server
Add support for fetching plan details from license server
2023-05-23 17:02:20 +03:00
899d46514c Add forwarding usedSeats and subscription quantity to license server on org member add/delete 2023-05-23 16:59:13 +03:00
658df21189 Add auto install pre commit 2023-05-23 00:09:00 -04:00
8341faddc5 Add support for pulling plan details from license server with LICENSE_KEY, LICENSE_SERVER_KEY 2023-05-22 15:43:33 +03:00
8e3a23e6d8 fix prod node img for standalone 2023-05-22 08:18:50 -04:00
1c89474159 hello 2023-05-19 17:23:15 -04:00
2f765600b1 add pre-commit hook 2023-05-19 17:20:27 -04:00
d9057216b5 remove keyring access during telemetry 2023-05-19 16:10:59 -04:00
6aab90590f add version to cli run telemtry 2023-05-19 12:24:49 -04:00
f7466d4855 update cli telemetry 2023-05-19 12:20:37 -04:00
ea2565ed35 Merge pull request #591 from Infisical/cli-telemetry
Cli telemetry
2023-05-19 10:55:27 -04:00
4586656b85 add post hog api to go releaser and update cli telemetry 2023-05-19 10:49:57 -04:00
e4953398df add telemetry to cli 2023-05-19 00:16:26 -04:00
7722231656 Merge pull request #590 from Infisical/infisical-scan-docs
Infisical scan docs
2023-05-18 15:59:51 -04:00
845a476974 add secret scanning to README.md 2023-05-18 15:57:48 -04:00
fc19a17f4b update readme with scaning feature 2023-05-18 15:42:25 -04:00
0890b1912f Merge pull request #589 from Infisical/infisical-scan-docs
add docs for infisical scan
2023-05-18 15:20:26 -04:00
82ecc2d7dc add secret scanning to resources 2023-05-18 15:18:29 -04:00
460bdbb91c Merge pull request #587 from Infisical/snyk-upgrade-76cf9e766d00cfa629a2db56d3b5fc39
[Snyk] Upgrade posthog-js from 1.53.4 to 1.54.0
2023-05-18 14:57:16 -04:00
446a63a917 add docs for infisical scan 2023-05-18 14:55:39 -04:00
d67cb7b507 Merge pull request #588 from Infisical/add-gitleak
rebrand and small tweeks
2023-05-18 12:07:26 -04:00
9f40266f5c fix: upgrade posthog-js from 1.53.4 to 1.54.0
Snyk has created this PR to upgrade posthog-js from 1.53.4 to 1.54.0.

See this package in npm:
https://www.npmjs.com/package/posthog-js

See this project in Snyk:
https://app.snyk.io/org/maidul98/project/53d4ecb6-6cc1-4918-aa73-bf9cae4ffd13?utm_source=github&utm_medium=referral&page=upgrade-pr
2023-05-18 11:58:06 +00:00
8af8a1d3d5 Merge pull request #580 from Infisical/add-gitleak
add gitleak to cli
2023-05-17 13:20:40 -04:00
631423fbc8 Merge pull request #583 from Infisical/snyk-upgrade-32d764d8893bf7596281cd2751bb5f9b
[Snyk] Upgrade @aws-sdk/client-secrets-manager from 3.317.0 to 3.319.0
2023-05-17 13:11:54 -04:00
4383779377 Merge pull request #581 from Infisical/snyk-upgrade-efa6b99248f4e9459845f26b359fc5c8
[Snyk] Upgrade aws-sdk from 2.1362.0 to 2.1364.0
2023-05-17 13:11:37 -04:00
20294ee233 Fixed the const issue 2023-05-17 09:27:12 -07:00
c5a924e935 Merge pull request #585 from Infisical/gitlab-envs
Add support for custom environments in GitLab integration
2023-05-17 14:31:00 +03:00
429bfd27b2 Add support for custom environments in GitLab integration 2023-05-17 14:25:18 +03:00
c99c873d78 fix: upgrade @aws-sdk/client-secrets-manager from 3.317.0 to 3.319.0
Snyk has created this PR to upgrade @aws-sdk/client-secrets-manager from 3.317.0 to 3.319.0.

See this package in npm:
https://www.npmjs.com/package/@aws-sdk/client-secrets-manager

See this project in Snyk:
https://app.snyk.io/org/maidul98/project/35057e82-ed7d-4e19-ba4d-719a42135cd6?utm_source=github&utm_medium=referral&page=upgrade-pr
2023-05-16 19:34:58 +00:00
092a6911ce fix: upgrade aws-sdk from 2.1362.0 to 2.1364.0
Snyk has created this PR to upgrade aws-sdk from 2.1362.0 to 2.1364.0.

See this package in npm:
https://www.npmjs.com/package/aws-sdk

See this project in Snyk:
https://app.snyk.io/org/maidul98/project/35057e82-ed7d-4e19-ba4d-719a42135cd6?utm_source=github&utm_medium=referral&page=upgrade-pr
2023-05-16 19:34:50 +00:00
a9b642e618 Merge branch 'main' of https://github.com/Infisical/infisical 2023-05-15 16:34:02 -07:00
919ddf5de2 removed console log 2023-05-15 16:33:44 -07:00
89a89af4e6 improving UX for the onboarding experience 2023-05-15 16:33:11 -07:00
960063e61a Merge pull request #574 from Infisical/snyk-upgrade-e333c5ab909cc9a88c7a6d9fc95a58ed
[Snyk] Upgrade @aws-sdk/client-secrets-manager from 3.312.0 to 3.317.0
2023-05-15 17:48:35 -04:00
abf4eaf6db Merge pull request #538 from Infisical/snyk-upgrade-64f35bb43ebf5a87403747b8d7956c34
[Snyk] Upgrade fs from 0.0.1-security to 0.0.2
2023-05-15 17:48:25 -04:00
739f97f5c9 Merge pull request #575 from Infisical/snyk-upgrade-6fd25092a72767ffb9954920488a4cd5
[Snyk] Upgrade @sentry/node from 7.47.0 to 7.49.0
2023-05-15 17:48:06 -04:00
faed5c1821 Merge pull request #576 from Infisical/snyk-upgrade-f1503bf1fac2c534c106f41288ce944d
[Snyk] Upgrade aws-sdk from 2.1360.0 to 2.1362.0
2023-05-15 17:47:54 -04:00
c95598aaa6 Merge pull request #578 from akhilmhdh/fix/compose-fail
fix: docker-compose failing due to missing frontend i18n file
2023-05-15 17:47:33 -04:00
e791684f4d fix: docker-compose failing due to missing frontend i18n file 2023-05-16 00:19:03 +05:30
d32c5fb869 update the dev stripe product id 2023-05-15 07:31:17 -07:00
abbf1918dc Added limits to the number of projects in an org 2023-05-14 18:25:27 -07:00
876d0119d3 Merge pull request #564 from parthvnp/feature/457
Add example in CLI usage docs to show how to utilize secrets in shell aliases
2023-05-13 11:23:27 -04:00
6d70dc437e update cli usage docs 2023-05-13 11:22:38 -04:00
174e22a2bc put aliases docs in Accordion 2023-05-13 11:17:17 -04:00
f4815641d8 fixed the bug with smaller icon buttons 2023-05-11 18:11:04 -07:00
5b95c255ec fix: upgrade aws-sdk from 2.1360.0 to 2.1362.0
Snyk has created this PR to upgrade aws-sdk from 2.1360.0 to 2.1362.0.

See this package in npm:
https://www.npmjs.com/package/aws-sdk

See this project in Snyk:
https://app.snyk.io/org/maidul98/project/35057e82-ed7d-4e19-ba4d-719a42135cd6?utm_source=github&utm_medium=referral&page=upgrade-pr
2023-05-11 22:13:48 +00:00
3123f6fc1f fix: upgrade @sentry/node from 7.47.0 to 7.49.0
Snyk has created this PR to upgrade @sentry/node from 7.47.0 to 7.49.0.

See this package in npm:
https://www.npmjs.com/package/@sentry/node

See this project in Snyk:
https://app.snyk.io/org/maidul98/project/35057e82-ed7d-4e19-ba4d-719a42135cd6?utm_source=github&utm_medium=referral&page=upgrade-pr
2023-05-11 22:13:44 +00:00
a913cd97a4 fix: upgrade @aws-sdk/client-secrets-manager from 3.312.0 to 3.317.0
Snyk has created this PR to upgrade @aws-sdk/client-secrets-manager from 3.312.0 to 3.317.0.

See this package in npm:
https://www.npmjs.com/package/@aws-sdk/client-secrets-manager

See this project in Snyk:
https://app.snyk.io/org/maidul98/project/35057e82-ed7d-4e19-ba4d-719a42135cd6?utm_source=github&utm_medium=referral&page=upgrade-pr
2023-05-11 22:13:39 +00:00
7aa5ef844c Update CLI usage docs to showcase the ability to inject environment variables in shell aliases 2023-05-08 01:04:35 -04:00
fd9387a25e fix: upgrade fs from 0.0.1-security to 0.0.2
Snyk has created this PR to upgrade fs from 0.0.1-security to 0.0.2.

See this package in npm:
https://www.npmjs.com/package/fs

See this project in Snyk:
https://app.snyk.io/org/maidul98/project/53d4ecb6-6cc1-4918-aa73-bf9cae4ffd13?utm_source=github&utm_medium=referral&page=upgrade-pr
2023-04-28 20:41:59 +00:00
86 changed files with 2134 additions and 502 deletions

2
.dockerignore Normal file
View File

@ -0,0 +1,2 @@
backend/node_modules
frontend/node_modules

View File

@ -13,6 +13,7 @@ jobs:
check-be-pr:
name: Check
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: ☁️ Checkout source
@ -26,17 +27,17 @@ jobs:
- name: 📦 Install dependencies
run: npm ci --only-production
working-directory: backend
- name: 🧪 Run tests
run: npm run test:ci
working-directory: backend
- name: 📁 Upload test results
uses: actions/upload-artifact@v3
if: always()
with:
name: be-test-results
path: |
./backend/reports
./backend/coverage
# - name: 🧪 Run tests
# run: npm run test:ci
# working-directory: backend
# - name: 📁 Upload test results
# uses: actions/upload-artifact@v3
# if: always()
# with:
# name: be-test-results
# path: |
# ./backend/reports
# ./backend/coverage
- name: 🏗️ Run build
run: npm run build
working-directory: backend

View File

@ -2,40 +2,35 @@ name: Check Frontend Pull Request
on:
pull_request:
types: [ opened, synchronize ]
types: [opened, synchronize]
paths:
- 'frontend/**'
- '!frontend/README.md'
- '!frontend/.*'
- 'frontend/.eslintrc.js'
- "frontend/**"
- "!frontend/README.md"
- "!frontend/.*"
- "frontend/.eslintrc.js"
jobs:
check-fe-pr:
name: Check
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
-
name: ☁️ Checkout source
- name: ☁️ Checkout source
uses: actions/checkout@v3
-
name: 🔧 Setup Node 16
- name: 🔧 Setup Node 16
uses: actions/setup-node@v3
with:
node-version: '16'
cache: 'npm'
node-version: "16"
cache: "npm"
cache-dependency-path: frontend/package-lock.json
-
name: 📦 Install dependencies
- name: 📦 Install dependencies
run: npm ci --only-production --ignore-scripts
working-directory: frontend
# -
# name: 🧪 Run tests
# run: npm run test:ci
# working-directory: frontend
-
name: 🏗️ Run build
- name: 🏗️ Run build
run: npm run build
working-directory: frontend

View File

@ -46,6 +46,7 @@ jobs:
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GO_RELEASER_GITHUB_TOKEN }}
POSTHOG_API_KEY_FOR_CLI: ${{ secrets.POSTHOG_API_KEY_FOR_CLI }}
FURY_TOKEN: ${{ secrets.FURYPUSHTOKEN }}
AUR_KEY: ${{ secrets.AUR_KEY }}
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}

View File

@ -18,7 +18,9 @@ monorepo:
builds:
- id: darwin-build
binary: infisical
ldflags: -X github.com/Infisical/infisical-merge/packages/util.CLI_VERSION={{ .Version }}
ldflags:
- -X github.com/Infisical/infisical-merge/packages/util.CLI_VERSION={{ .Version }}
- -X github.com/Infisical/infisical-merge/packages/telemetry.POSTHOG_API_KEY_FOR_CLI={{ .Env.POSTHOG_API_KEY_FOR_CLI }}
flags:
- -trimpath
env:
@ -36,7 +38,9 @@ builds:
env:
- CGO_ENABLED=0
binary: infisical
ldflags: -X github.com/Infisical/infisical-merge/packages/util.CLI_VERSION={{ .Version }}
ldflags:
- -X github.com/Infisical/infisical-merge/packages/util.CLI_VERSION={{ .Version }}
- -X github.com/Infisical/infisical-merge/packages/telemetry.POSTHOG_API_KEY_FOR_CLI={{ .Env.POSTHOG_API_KEY_FOR_CLI }}
flags:
- -trimpath
goos:

View File

@ -3,3 +3,5 @@
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged
infisical scan git-changes --staged -v

5
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,5 @@
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.16.3
hooks:
- id: gitleaks

6
.pre-commit-hooks.yaml Normal file
View File

@ -0,0 +1,6 @@
- id: infisical-scan
name: Scan for hardcoded secrets
description: Will scan for hardcoded secrets using Infisical CLI
entry: infisical scan git-changes --verbose --redact --staged
language: golang
pass_filenames: false

View File

@ -77,7 +77,7 @@ RUN npm ci --only-production
COPY --from=backend-build /app .
# Production stage
FROM node:14-alpine AS production
FROM node:16-alpine AS production
WORKDIR /

View File

@ -89,6 +89,24 @@ git clone https://github.com/Infisical/infisical && cd infisical && copy .env.ex
Create an account at `http://localhost:80`
### Scan and prevent secret leaks
On top managing secrets with Infisical, you can also scan for over 140+ secret types in your files, directories and git repositories.
To scan your full git history, run:
```
infisical scan --verbose
```
Install pre commit hook to scan each commit before you push to your repository
```
infisical scan install --pre-commit-hook
```
Lean about Infisical's code scanning feature [here](https://infisical.com/docs/cli/scanning-overview)
## Open-source vs. paid
This repo available under the [MIT expat license](https://github.com/Infisical/infisical/blob/main/LICENSE), with the exception of the `ee` directory which will contain premium enterprise features requiring a Infisical license in the future.

View File

@ -9,16 +9,16 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"@aws-sdk/client-secrets-manager": "^3.312.0",
"@aws-sdk/client-secrets-manager": "^3.319.0",
"@godaddy/terminus": "^4.12.0",
"@octokit/rest": "^19.0.5",
"@sentry/node": "^7.41.0",
"@sentry/node": "^7.49.0",
"@sentry/tracing": "^7.48.0",
"@types/crypto-js": "^4.1.1",
"@types/libsodium-wrappers": "^0.7.10",
"argon2": "^0.30.3",
"await-to-js": "^3.0.0",
"aws-sdk": "^2.1360.0",
"aws-sdk": "^2.1364.0",
"axios": "^1.3.5",
"axios-retry": "^3.4.0",
"bcrypt": "^5.1.0",
@ -40,6 +40,7 @@
"libsodium-wrappers": "^0.7.10",
"lodash": "^4.17.21",
"mongoose": "^6.10.5",
"node-cache": "^5.1.2",
"nodemailer": "^6.8.0",
"posthog-node": "^2.6.0",
"query-string": "^7.1.3",
@ -234,15 +235,15 @@
}
},
"node_modules/@aws-sdk/client-secrets-manager": {
"version": "3.312.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.312.0.tgz",
"integrity": "sha512-8MhjP8xXU9pWnWi43YvsU/EIj9R4ABUo+XpNsxHkR9KTsG2QR/0h/xjQO9hqZOukZYW8DwdwLRxH81FsDTLYEQ==",
"version": "3.319.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.319.0.tgz",
"integrity": "sha512-3YPzBvr/hRlH3MbivWYGdvY4EMLwBVAE93gZjfIrkjvpufd/OKX5OoO8Q0EhOkfnc2VIw0DMT2x6ERtgXuVc4Q==",
"dependencies": {
"@aws-crypto/sha256-browser": "3.0.0",
"@aws-crypto/sha256-js": "3.0.0",
"@aws-sdk/client-sts": "3.312.0",
"@aws-sdk/client-sts": "3.319.0",
"@aws-sdk/config-resolver": "3.310.0",
"@aws-sdk/credential-provider-node": "3.310.0",
"@aws-sdk/credential-provider-node": "3.319.0",
"@aws-sdk/fetch-http-handler": "3.310.0",
"@aws-sdk/hash-node": "3.310.0",
"@aws-sdk/invalid-dependency": "3.310.0",
@ -255,19 +256,19 @@
"@aws-sdk/middleware-serde": "3.310.0",
"@aws-sdk/middleware-signing": "3.310.0",
"@aws-sdk/middleware-stack": "3.310.0",
"@aws-sdk/middleware-user-agent": "3.310.0",
"@aws-sdk/middleware-user-agent": "3.319.0",
"@aws-sdk/node-config-provider": "3.310.0",
"@aws-sdk/node-http-handler": "3.310.0",
"@aws-sdk/protocol-http": "3.310.0",
"@aws-sdk/smithy-client": "3.310.0",
"@aws-sdk/smithy-client": "3.316.0",
"@aws-sdk/types": "3.310.0",
"@aws-sdk/url-parser": "3.310.0",
"@aws-sdk/util-base64": "3.310.0",
"@aws-sdk/util-body-length-browser": "3.310.0",
"@aws-sdk/util-body-length-node": "3.310.0",
"@aws-sdk/util-defaults-mode-browser": "3.310.0",
"@aws-sdk/util-defaults-mode-node": "3.310.0",
"@aws-sdk/util-endpoints": "3.310.0",
"@aws-sdk/util-defaults-mode-browser": "3.316.0",
"@aws-sdk/util-defaults-mode-node": "3.316.0",
"@aws-sdk/util-endpoints": "3.319.0",
"@aws-sdk/util-retry": "3.310.0",
"@aws-sdk/util-user-agent-browser": "3.310.0",
"@aws-sdk/util-user-agent-node": "3.310.0",
@ -292,9 +293,9 @@
}
},
"node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/client-sso": {
"version": "3.310.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.310.0.tgz",
"integrity": "sha512-netFap3Mp9I7bzAjsswHPA5WEbQtNMmXvW9/IVb7tmf85/esXCWindtyI43e/Xerut9ZVyEACPBFn30CLLE2xQ==",
"version": "3.319.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.319.0.tgz",
"integrity": "sha512-g46KgAjRiYBS8Oi85DPwSAQpt+Hgmw/YFgGVwZqMfTL70KNJwLFKRa5D9UocQd7t7OjPRdKF7g0Gp5peyAK9dw==",
"dependencies": {
"@aws-crypto/sha256-browser": "3.0.0",
"@aws-crypto/sha256-js": "3.0.0",
@ -310,19 +311,19 @@
"@aws-sdk/middleware-retry": "3.310.0",
"@aws-sdk/middleware-serde": "3.310.0",
"@aws-sdk/middleware-stack": "3.310.0",
"@aws-sdk/middleware-user-agent": "3.310.0",
"@aws-sdk/middleware-user-agent": "3.319.0",
"@aws-sdk/node-config-provider": "3.310.0",
"@aws-sdk/node-http-handler": "3.310.0",
"@aws-sdk/protocol-http": "3.310.0",
"@aws-sdk/smithy-client": "3.310.0",
"@aws-sdk/smithy-client": "3.316.0",
"@aws-sdk/types": "3.310.0",
"@aws-sdk/url-parser": "3.310.0",
"@aws-sdk/util-base64": "3.310.0",
"@aws-sdk/util-body-length-browser": "3.310.0",
"@aws-sdk/util-body-length-node": "3.310.0",
"@aws-sdk/util-defaults-mode-browser": "3.310.0",
"@aws-sdk/util-defaults-mode-node": "3.310.0",
"@aws-sdk/util-endpoints": "3.310.0",
"@aws-sdk/util-defaults-mode-browser": "3.316.0",
"@aws-sdk/util-defaults-mode-node": "3.316.0",
"@aws-sdk/util-endpoints": "3.319.0",
"@aws-sdk/util-retry": "3.310.0",
"@aws-sdk/util-user-agent-browser": "3.310.0",
"@aws-sdk/util-user-agent-node": "3.310.0",
@ -334,9 +335,9 @@
}
},
"node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/client-sso-oidc": {
"version": "3.310.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.310.0.tgz",
"integrity": "sha512-3GKaRSfMD3OiYWGa+qg5KvJw0nLV0Vu7zRiulLuKDvgmWw3SNJKn3frWlmq/bKFUKahLsV8zozbeJItxtKAD6g==",
"version": "3.319.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.319.0.tgz",
"integrity": "sha512-GJBgT/tephRZY3oTbDBMv+G9taoqKUIvGPn+7shmzz2P1SerutsRSfKfDXV+VptPNRoGmjjCLPmWjMFYbFKILQ==",
"dependencies": {
"@aws-crypto/sha256-browser": "3.0.0",
"@aws-crypto/sha256-js": "3.0.0",
@ -352,19 +353,19 @@
"@aws-sdk/middleware-retry": "3.310.0",
"@aws-sdk/middleware-serde": "3.310.0",
"@aws-sdk/middleware-stack": "3.310.0",
"@aws-sdk/middleware-user-agent": "3.310.0",
"@aws-sdk/middleware-user-agent": "3.319.0",
"@aws-sdk/node-config-provider": "3.310.0",
"@aws-sdk/node-http-handler": "3.310.0",
"@aws-sdk/protocol-http": "3.310.0",
"@aws-sdk/smithy-client": "3.310.0",
"@aws-sdk/smithy-client": "3.316.0",
"@aws-sdk/types": "3.310.0",
"@aws-sdk/url-parser": "3.310.0",
"@aws-sdk/util-base64": "3.310.0",
"@aws-sdk/util-body-length-browser": "3.310.0",
"@aws-sdk/util-body-length-node": "3.310.0",
"@aws-sdk/util-defaults-mode-browser": "3.310.0",
"@aws-sdk/util-defaults-mode-node": "3.310.0",
"@aws-sdk/util-endpoints": "3.310.0",
"@aws-sdk/util-defaults-mode-browser": "3.316.0",
"@aws-sdk/util-defaults-mode-node": "3.316.0",
"@aws-sdk/util-endpoints": "3.319.0",
"@aws-sdk/util-retry": "3.310.0",
"@aws-sdk/util-user-agent-browser": "3.310.0",
"@aws-sdk/util-user-agent-node": "3.310.0",
@ -376,14 +377,14 @@
}
},
"node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/client-sts": {
"version": "3.312.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.312.0.tgz",
"integrity": "sha512-t0U7vRvWaMjrzBUo6tPrHe6HE97Blqx+b4GOjFbcbLtzxLlcRfhnWJik0Lp8hJtVqzNoN5mL4OeYgK7CRpL/Sw==",
"version": "3.319.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.319.0.tgz",
"integrity": "sha512-PRGGKCSKtyM3x629J9j4DMsH1cQT8UGW+R67u9Q5HrMK05gfjpmg+X1DQ3pgve4D8MI4R/Cm3NkYl2eUTbQHQg==",
"dependencies": {
"@aws-crypto/sha256-browser": "3.0.0",
"@aws-crypto/sha256-js": "3.0.0",
"@aws-sdk/config-resolver": "3.310.0",
"@aws-sdk/credential-provider-node": "3.310.0",
"@aws-sdk/credential-provider-node": "3.319.0",
"@aws-sdk/fetch-http-handler": "3.310.0",
"@aws-sdk/hash-node": "3.310.0",
"@aws-sdk/invalid-dependency": "3.310.0",
@ -397,19 +398,19 @@
"@aws-sdk/middleware-serde": "3.310.0",
"@aws-sdk/middleware-signing": "3.310.0",
"@aws-sdk/middleware-stack": "3.310.0",
"@aws-sdk/middleware-user-agent": "3.310.0",
"@aws-sdk/middleware-user-agent": "3.319.0",
"@aws-sdk/node-config-provider": "3.310.0",
"@aws-sdk/node-http-handler": "3.310.0",
"@aws-sdk/protocol-http": "3.310.0",
"@aws-sdk/smithy-client": "3.310.0",
"@aws-sdk/smithy-client": "3.316.0",
"@aws-sdk/types": "3.310.0",
"@aws-sdk/url-parser": "3.310.0",
"@aws-sdk/util-base64": "3.310.0",
"@aws-sdk/util-body-length-browser": "3.310.0",
"@aws-sdk/util-body-length-node": "3.310.0",
"@aws-sdk/util-defaults-mode-browser": "3.310.0",
"@aws-sdk/util-defaults-mode-node": "3.310.0",
"@aws-sdk/util-endpoints": "3.310.0",
"@aws-sdk/util-defaults-mode-browser": "3.316.0",
"@aws-sdk/util-defaults-mode-node": "3.316.0",
"@aws-sdk/util-endpoints": "3.319.0",
"@aws-sdk/util-retry": "3.310.0",
"@aws-sdk/util-user-agent-browser": "3.310.0",
"@aws-sdk/util-user-agent-node": "3.310.0",
@ -464,14 +465,14 @@
}
},
"node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/credential-provider-ini": {
"version": "3.310.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.310.0.tgz",
"integrity": "sha512-gtRz7I+4BBpwZ3tc6UIt5lQuiAFnkpOibxHh95x1M6HDxBjm+uqD6RPZYVH+dULZPYXOtOTsHV0IGjrcV0sSRg==",
"version": "3.319.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.319.0.tgz",
"integrity": "sha512-pzx388Fw1KlSgmIMUyRY8DJVYM3aXpwzjprD4RiQVPJeAI+t7oQmEvd2FiUZEuHDjWXcuonxgU+dk7i7HUk/HQ==",
"dependencies": {
"@aws-sdk/credential-provider-env": "3.310.0",
"@aws-sdk/credential-provider-imds": "3.310.0",
"@aws-sdk/credential-provider-process": "3.310.0",
"@aws-sdk/credential-provider-sso": "3.310.0",
"@aws-sdk/credential-provider-sso": "3.319.0",
"@aws-sdk/credential-provider-web-identity": "3.310.0",
"@aws-sdk/property-provider": "3.310.0",
"@aws-sdk/shared-ini-file-loader": "3.310.0",
@ -483,15 +484,15 @@
}
},
"node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/credential-provider-node": {
"version": "3.310.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.310.0.tgz",
"integrity": "sha512-FrOztUcOq2Sp32xGtJvxfvdlmuAeoxIu/AElHzV1bkx6Pzo9DkQBhXrSQ+JFSpI++weOD4ZGFhAvgbgUOT4VAg==",
"version": "3.319.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.319.0.tgz",
"integrity": "sha512-DS4a0Rdd7ZtMshoeE+zuSgbC05YBcdzd0h89u/eX+1Yqx+HCjeb8WXkbXsz0Mwx8q9TE04aS8f6Bw9J4x4mO5g==",
"dependencies": {
"@aws-sdk/credential-provider-env": "3.310.0",
"@aws-sdk/credential-provider-imds": "3.310.0",
"@aws-sdk/credential-provider-ini": "3.310.0",
"@aws-sdk/credential-provider-ini": "3.319.0",
"@aws-sdk/credential-provider-process": "3.310.0",
"@aws-sdk/credential-provider-sso": "3.310.0",
"@aws-sdk/credential-provider-sso": "3.319.0",
"@aws-sdk/credential-provider-web-identity": "3.310.0",
"@aws-sdk/property-provider": "3.310.0",
"@aws-sdk/shared-ini-file-loader": "3.310.0",
@ -517,14 +518,14 @@
}
},
"node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/credential-provider-sso": {
"version": "3.310.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.310.0.tgz",
"integrity": "sha512-nXkpT8mrM/wRqSiz/a4p9U2UrOKyfZXhbPHIHyQj8K+uLjsYS+WPuH287J4A5Q57A6uarTrj5RjHmVeZVLaHmg==",
"version": "3.319.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.319.0.tgz",
"integrity": "sha512-gAUnWH41lxkIbANXu+Rz5zS0Iavjjmpf3C56vAMT7oaYZ3Cg/Ys5l2SwAucQGOCA2DdS2hDiSI8E+Yhr4F5toA==",
"dependencies": {
"@aws-sdk/client-sso": "3.310.0",
"@aws-sdk/client-sso": "3.319.0",
"@aws-sdk/property-provider": "3.310.0",
"@aws-sdk/shared-ini-file-loader": "3.310.0",
"@aws-sdk/token-providers": "3.310.0",
"@aws-sdk/token-providers": "3.319.0",
"@aws-sdk/types": "3.310.0",
"tslib": "^2.5.0"
},
@ -727,13 +728,13 @@
}
},
"node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/middleware-user-agent": {
"version": "3.310.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.310.0.tgz",
"integrity": "sha512-x3IOwSwSbwKidlxRk3CNVHVUb06SRuaELxggCaR++QVI8NU6qD/l4VHXKVRvbTHiC/cYxXE/GaBDgQVpDR7V/g==",
"version": "3.319.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.319.0.tgz",
"integrity": "sha512-ytaLx2dlR5AdMSne6FuDCISVg8hjyKj+cHU20b2CRA/E/z+XXrLrssp4JrCgizRKPPUep0psMIa22Zd6osTT5Q==",
"dependencies": {
"@aws-sdk/protocol-http": "3.310.0",
"@aws-sdk/types": "3.310.0",
"@aws-sdk/util-endpoints": "3.310.0",
"@aws-sdk/util-endpoints": "3.319.0",
"tslib": "^2.5.0"
},
"engines": {
@ -856,9 +857,9 @@
}
},
"node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/smithy-client": {
"version": "3.310.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.310.0.tgz",
"integrity": "sha512-UHMFvhoB2RLzsTb0mQe1ofvBUg/+/JEu1uptavxf/hEpEKZnRAaHH5FNkTG+mbFd/olay/QFjqNcMD6t8LcsNQ==",
"version": "3.316.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.316.0.tgz",
"integrity": "sha512-6YXOKbRnXeS8r8RWzuL6JMBolDYM5Wa4fD/VY6x/wK78i2xErHOvqzHgyyeLI1MMw4uqyd4wRNJNWC9TMPduXw==",
"dependencies": {
"@aws-sdk/middleware-stack": "3.310.0",
"@aws-sdk/types": "3.310.0",
@ -869,11 +870,11 @@
}
},
"node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/token-providers": {
"version": "3.310.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.310.0.tgz",
"integrity": "sha512-G1JvB+2v8k900VJFkKVQXgLGF50ShOEIPxfK1gSQLkSU85vPwGIAANs1KvnlW08FsNbWp3+sKca4kfYKsooXMw==",
"version": "3.319.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.319.0.tgz",
"integrity": "sha512-5utg6VL6Pl0uiLUn8ZJPYYxzCb9VRPsgJmGXktRUwq0YlTJ6ABcaxTXwZcC++sjh/qyCQDK5PPLNU5kIBttHMQ==",
"dependencies": {
"@aws-sdk/client-sso-oidc": "3.310.0",
"@aws-sdk/client-sso-oidc": "3.319.0",
"@aws-sdk/property-provider": "3.310.0",
"@aws-sdk/shared-ini-file-loader": "3.310.0",
"@aws-sdk/types": "3.310.0",
@ -959,9 +960,9 @@
}
},
"node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/util-defaults-mode-browser": {
"version": "3.310.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.310.0.tgz",
"integrity": "sha512-Mr2AoQsjAYNM5oAS2YJlYJqhiCvkFV/hu48slOZgbY4G7ueW4cM0DPkR16wqjcRCGqZ4JmAZB8Q5R0DMrLjhOQ==",
"version": "3.316.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.316.0.tgz",
"integrity": "sha512-6FSqLhYmaihtH2n1s4b2rlLW0ABU8N6VZIfzLfe2ING4PF0MzfaMMhnTFUHVXfKCVGoR8yP6iyFTRCyHGVEL1w==",
"dependencies": {
"@aws-sdk/property-provider": "3.310.0",
"@aws-sdk/types": "3.310.0",
@ -973,9 +974,9 @@
}
},
"node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/util-defaults-mode-node": {
"version": "3.310.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.310.0.tgz",
"integrity": "sha512-JyBlvhQGR8w8NpFRZZXRVTDesafFKTu/gTWjcoxP7twa+fYHSIgPPFGnlcJ/iHaucjamSaWi5EQ+YQmnSZ8yHA==",
"version": "3.316.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.316.0.tgz",
"integrity": "sha512-dkYy10hdjPSScXXvnjGpZpnJxllkb6ICHgLMwZ4JczLHhPM12T/4PQ758YN8HS+muiYDGX1Bl2z1jd/bMcewBQ==",
"dependencies": {
"@aws-sdk/config-resolver": "3.310.0",
"@aws-sdk/credential-provider-imds": "3.310.0",
@ -989,9 +990,9 @@
}
},
"node_modules/@aws-sdk/client-secrets-manager/node_modules/@aws-sdk/util-endpoints": {
"version": "3.310.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.310.0.tgz",
"integrity": "sha512-zG+/d/O5KPmAaeOMPd6bW1abifdT0H03f42keLjYEoRZzYtHPC5DuPE0UayiWGckI6BCDgy0sRKXCYS49UNFaQ==",
"version": "3.319.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.319.0.tgz",
"integrity": "sha512-3I64UMoYA2e2++oOUJXRcFtYLpLylnZFRltWfPo1B3dLlf+MIWat9djT+mMus+hW1ntLsvAIVu1hLVePJC0gvw==",
"dependencies": {
"@aws-sdk/types": "3.310.0",
"tslib": "^2.5.0"
@ -3654,13 +3655,13 @@
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
},
"node_modules/@sentry-internal/tracing": {
"version": "7.47.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.47.0.tgz",
"integrity": "sha512-udpHnCzF8DQsWf0gQwd0XFGp6Y8MOiwnl8vGt2ohqZGS3m1+IxoRLXsSkD8qmvN6KKDnwbaAvYnK0z0L+AW95g==",
"version": "7.49.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.49.0.tgz",
"integrity": "sha512-ESh3+ZneQk/3HESTUmIPNrW5GVPu/HrRJU+eAJJto74vm+6vP7zDn2YV2gJ1w18O/37nc7W/bVCgZJlhZ3cwew==",
"dependencies": {
"@sentry/core": "7.47.0",
"@sentry/types": "7.47.0",
"@sentry/utils": "7.47.0",
"@sentry/core": "7.49.0",
"@sentry/types": "7.49.0",
"@sentry/utils": "7.49.0",
"tslib": "^1.9.3"
},
"engines": {
@ -3668,12 +3669,12 @@
}
},
"node_modules/@sentry-internal/tracing/node_modules/@sentry/core": {
"version": "7.47.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.47.0.tgz",
"integrity": "sha512-EFhZhKdMu7wKmWYZwbgTi8FNZ7Fq+HdlXiZWNz51Bqe3pHmfAkdHtAEs0Buo0v623MKA0CA4EjXIazGUM34XTg==",
"version": "7.49.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.49.0.tgz",
"integrity": "sha512-AlSnCYgfEbvK8pkNluUkmdW/cD9UpvOVCa+ERQswXNRkAv5aDGCL6Ihv6fnIajE++BYuwZh0+HwZUBVKTFzoZg==",
"dependencies": {
"@sentry/types": "7.47.0",
"@sentry/utils": "7.47.0",
"@sentry/types": "7.49.0",
"@sentry/utils": "7.49.0",
"tslib": "^1.9.3"
},
"engines": {
@ -3681,19 +3682,19 @@
}
},
"node_modules/@sentry-internal/tracing/node_modules/@sentry/types": {
"version": "7.47.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.47.0.tgz",
"integrity": "sha512-GxXocplN0j1+uczovHrfkykl9wvkamDtWxlPUQgyGlbLGZn+UH1Y79D4D58COaFWGEZdSNKr62gZAjfEYu9nQA==",
"version": "7.49.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.49.0.tgz",
"integrity": "sha512-9yXXh7iv76+O6h2ONUVx0wsL1auqJFWez62mTjWk4350SgMmWp/zUkBxnVXhmcYqscz/CepC+Loz9vITLXtgxg==",
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry-internal/tracing/node_modules/@sentry/utils": {
"version": "7.47.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.47.0.tgz",
"integrity": "sha512-A89SaOLp6XeZfByeYo2C8Ecye/YAtk/gENuyOUhQEdMulI6mZdjqtHAp7pTMVgkBc/YNARVuoa+kR/IdRrTPkQ==",
"version": "7.49.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.49.0.tgz",
"integrity": "sha512-JdC9yGnOgev4ISJVwmIoFsk8Zx0psDZJAj2DV7x4wMZsO6QK+YjC7G3mUED/S5D5lsrkBZ/3uvQQhr8DQI4UcQ==",
"dependencies": {
"@sentry/types": "7.47.0",
"@sentry/types": "7.49.0",
"tslib": "^1.9.3"
},
"engines": {
@ -3724,14 +3725,14 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/@sentry/node": {
"version": "7.47.0",
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.47.0.tgz",
"integrity": "sha512-LTg2r5EV9yh4GLYDF+ViSltR9LLj/pcvk8YhANJcMO3Fp//xh8njcdU0FC2yNthUREawYDzAsVzLyCYJfV0H1A==",
"version": "7.49.0",
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.49.0.tgz",
"integrity": "sha512-KLIrqcbKk4yR3g8fjl87Eyv4M9j4YI6b7sqVAZYj3FrX3mC6JQyGdlDfUpSKy604n1iAdr6OuUp5f9x7jPJaeQ==",
"dependencies": {
"@sentry-internal/tracing": "7.47.0",
"@sentry/core": "7.47.0",
"@sentry/types": "7.47.0",
"@sentry/utils": "7.47.0",
"@sentry-internal/tracing": "7.49.0",
"@sentry/core": "7.49.0",
"@sentry/types": "7.49.0",
"@sentry/utils": "7.49.0",
"cookie": "^0.4.1",
"https-proxy-agent": "^5.0.0",
"lru_map": "^0.3.3",
@ -3742,12 +3743,12 @@
}
},
"node_modules/@sentry/node/node_modules/@sentry/core": {
"version": "7.47.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.47.0.tgz",
"integrity": "sha512-EFhZhKdMu7wKmWYZwbgTi8FNZ7Fq+HdlXiZWNz51Bqe3pHmfAkdHtAEs0Buo0v623MKA0CA4EjXIazGUM34XTg==",
"version": "7.49.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.49.0.tgz",
"integrity": "sha512-AlSnCYgfEbvK8pkNluUkmdW/cD9UpvOVCa+ERQswXNRkAv5aDGCL6Ihv6fnIajE++BYuwZh0+HwZUBVKTFzoZg==",
"dependencies": {
"@sentry/types": "7.47.0",
"@sentry/utils": "7.47.0",
"@sentry/types": "7.49.0",
"@sentry/utils": "7.49.0",
"tslib": "^1.9.3"
},
"engines": {
@ -3755,19 +3756,19 @@
}
},
"node_modules/@sentry/node/node_modules/@sentry/types": {
"version": "7.47.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.47.0.tgz",
"integrity": "sha512-GxXocplN0j1+uczovHrfkykl9wvkamDtWxlPUQgyGlbLGZn+UH1Y79D4D58COaFWGEZdSNKr62gZAjfEYu9nQA==",
"version": "7.49.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.49.0.tgz",
"integrity": "sha512-9yXXh7iv76+O6h2ONUVx0wsL1auqJFWez62mTjWk4350SgMmWp/zUkBxnVXhmcYqscz/CepC+Loz9vITLXtgxg==",
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/node/node_modules/@sentry/utils": {
"version": "7.47.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.47.0.tgz",
"integrity": "sha512-A89SaOLp6XeZfByeYo2C8Ecye/YAtk/gENuyOUhQEdMulI6mZdjqtHAp7pTMVgkBc/YNARVuoa+kR/IdRrTPkQ==",
"version": "7.49.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.49.0.tgz",
"integrity": "sha512-JdC9yGnOgev4ISJVwmIoFsk8Zx0psDZJAj2DV7x4wMZsO6QK+YjC7G3mUED/S5D5lsrkBZ/3uvQQhr8DQI4UcQ==",
"dependencies": {
"@sentry/types": "7.47.0",
"@sentry/types": "7.49.0",
"tslib": "^1.9.3"
},
"engines": {
@ -4824,9 +4825,9 @@
}
},
"node_modules/aws-sdk": {
"version": "2.1360.0",
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1360.0.tgz",
"integrity": "sha512-wW1CviH1s6bl5+wO+KM7aSc3yy6cQPJT85Fd4rQgrn0uwfjg9fx7KJ0FRhv+eU4DabkRjcSMlKo1IGhARmT6Tw==",
"version": "2.1364.0",
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1364.0.tgz",
"integrity": "sha512-PFoq9Rnu0DVi07wZ/SjKlrJDwso8AxE5q/ufLdNtINZPaSkB92OqKYVdlfQ4srsH2ala2NCrg8gFX98SCmqW3w==",
"dependencies": {
"buffer": "4.9.2",
"events": "1.1.1",
@ -13398,15 +13399,15 @@
}
},
"@aws-sdk/client-secrets-manager": {
"version": "3.312.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.312.0.tgz",
"integrity": "sha512-8MhjP8xXU9pWnWi43YvsU/EIj9R4ABUo+XpNsxHkR9KTsG2QR/0h/xjQO9hqZOukZYW8DwdwLRxH81FsDTLYEQ==",
"version": "3.319.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.319.0.tgz",
"integrity": "sha512-3YPzBvr/hRlH3MbivWYGdvY4EMLwBVAE93gZjfIrkjvpufd/OKX5OoO8Q0EhOkfnc2VIw0DMT2x6ERtgXuVc4Q==",
"requires": {
"@aws-crypto/sha256-browser": "3.0.0",
"@aws-crypto/sha256-js": "3.0.0",
"@aws-sdk/client-sts": "3.312.0",
"@aws-sdk/client-sts": "3.319.0",
"@aws-sdk/config-resolver": "3.310.0",
"@aws-sdk/credential-provider-node": "3.310.0",
"@aws-sdk/credential-provider-node": "3.319.0",
"@aws-sdk/fetch-http-handler": "3.310.0",
"@aws-sdk/hash-node": "3.310.0",
"@aws-sdk/invalid-dependency": "3.310.0",
@ -13419,19 +13420,19 @@
"@aws-sdk/middleware-serde": "3.310.0",
"@aws-sdk/middleware-signing": "3.310.0",
"@aws-sdk/middleware-stack": "3.310.0",
"@aws-sdk/middleware-user-agent": "3.310.0",
"@aws-sdk/middleware-user-agent": "3.319.0",
"@aws-sdk/node-config-provider": "3.310.0",
"@aws-sdk/node-http-handler": "3.310.0",
"@aws-sdk/protocol-http": "3.310.0",
"@aws-sdk/smithy-client": "3.310.0",
"@aws-sdk/smithy-client": "3.316.0",
"@aws-sdk/types": "3.310.0",
"@aws-sdk/url-parser": "3.310.0",
"@aws-sdk/util-base64": "3.310.0",
"@aws-sdk/util-body-length-browser": "3.310.0",
"@aws-sdk/util-body-length-node": "3.310.0",
"@aws-sdk/util-defaults-mode-browser": "3.310.0",
"@aws-sdk/util-defaults-mode-node": "3.310.0",
"@aws-sdk/util-endpoints": "3.310.0",
"@aws-sdk/util-defaults-mode-browser": "3.316.0",
"@aws-sdk/util-defaults-mode-node": "3.316.0",
"@aws-sdk/util-endpoints": "3.319.0",
"@aws-sdk/util-retry": "3.310.0",
"@aws-sdk/util-user-agent-browser": "3.310.0",
"@aws-sdk/util-user-agent-node": "3.310.0",
@ -13450,9 +13451,9 @@
}
},
"@aws-sdk/client-sso": {
"version": "3.310.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.310.0.tgz",
"integrity": "sha512-netFap3Mp9I7bzAjsswHPA5WEbQtNMmXvW9/IVb7tmf85/esXCWindtyI43e/Xerut9ZVyEACPBFn30CLLE2xQ==",
"version": "3.319.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.319.0.tgz",
"integrity": "sha512-g46KgAjRiYBS8Oi85DPwSAQpt+Hgmw/YFgGVwZqMfTL70KNJwLFKRa5D9UocQd7t7OjPRdKF7g0Gp5peyAK9dw==",
"requires": {
"@aws-crypto/sha256-browser": "3.0.0",
"@aws-crypto/sha256-js": "3.0.0",
@ -13468,19 +13469,19 @@
"@aws-sdk/middleware-retry": "3.310.0",
"@aws-sdk/middleware-serde": "3.310.0",
"@aws-sdk/middleware-stack": "3.310.0",
"@aws-sdk/middleware-user-agent": "3.310.0",
"@aws-sdk/middleware-user-agent": "3.319.0",
"@aws-sdk/node-config-provider": "3.310.0",
"@aws-sdk/node-http-handler": "3.310.0",
"@aws-sdk/protocol-http": "3.310.0",
"@aws-sdk/smithy-client": "3.310.0",
"@aws-sdk/smithy-client": "3.316.0",
"@aws-sdk/types": "3.310.0",
"@aws-sdk/url-parser": "3.310.0",
"@aws-sdk/util-base64": "3.310.0",
"@aws-sdk/util-body-length-browser": "3.310.0",
"@aws-sdk/util-body-length-node": "3.310.0",
"@aws-sdk/util-defaults-mode-browser": "3.310.0",
"@aws-sdk/util-defaults-mode-node": "3.310.0",
"@aws-sdk/util-endpoints": "3.310.0",
"@aws-sdk/util-defaults-mode-browser": "3.316.0",
"@aws-sdk/util-defaults-mode-node": "3.316.0",
"@aws-sdk/util-endpoints": "3.319.0",
"@aws-sdk/util-retry": "3.310.0",
"@aws-sdk/util-user-agent-browser": "3.310.0",
"@aws-sdk/util-user-agent-node": "3.310.0",
@ -13489,9 +13490,9 @@
}
},
"@aws-sdk/client-sso-oidc": {
"version": "3.310.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.310.0.tgz",
"integrity": "sha512-3GKaRSfMD3OiYWGa+qg5KvJw0nLV0Vu7zRiulLuKDvgmWw3SNJKn3frWlmq/bKFUKahLsV8zozbeJItxtKAD6g==",
"version": "3.319.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.319.0.tgz",
"integrity": "sha512-GJBgT/tephRZY3oTbDBMv+G9taoqKUIvGPn+7shmzz2P1SerutsRSfKfDXV+VptPNRoGmjjCLPmWjMFYbFKILQ==",
"requires": {
"@aws-crypto/sha256-browser": "3.0.0",
"@aws-crypto/sha256-js": "3.0.0",
@ -13507,19 +13508,19 @@
"@aws-sdk/middleware-retry": "3.310.0",
"@aws-sdk/middleware-serde": "3.310.0",
"@aws-sdk/middleware-stack": "3.310.0",
"@aws-sdk/middleware-user-agent": "3.310.0",
"@aws-sdk/middleware-user-agent": "3.319.0",
"@aws-sdk/node-config-provider": "3.310.0",
"@aws-sdk/node-http-handler": "3.310.0",
"@aws-sdk/protocol-http": "3.310.0",
"@aws-sdk/smithy-client": "3.310.0",
"@aws-sdk/smithy-client": "3.316.0",
"@aws-sdk/types": "3.310.0",
"@aws-sdk/url-parser": "3.310.0",
"@aws-sdk/util-base64": "3.310.0",
"@aws-sdk/util-body-length-browser": "3.310.0",
"@aws-sdk/util-body-length-node": "3.310.0",
"@aws-sdk/util-defaults-mode-browser": "3.310.0",
"@aws-sdk/util-defaults-mode-node": "3.310.0",
"@aws-sdk/util-endpoints": "3.310.0",
"@aws-sdk/util-defaults-mode-browser": "3.316.0",
"@aws-sdk/util-defaults-mode-node": "3.316.0",
"@aws-sdk/util-endpoints": "3.319.0",
"@aws-sdk/util-retry": "3.310.0",
"@aws-sdk/util-user-agent-browser": "3.310.0",
"@aws-sdk/util-user-agent-node": "3.310.0",
@ -13528,14 +13529,14 @@
}
},
"@aws-sdk/client-sts": {
"version": "3.312.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.312.0.tgz",
"integrity": "sha512-t0U7vRvWaMjrzBUo6tPrHe6HE97Blqx+b4GOjFbcbLtzxLlcRfhnWJik0Lp8hJtVqzNoN5mL4OeYgK7CRpL/Sw==",
"version": "3.319.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.319.0.tgz",
"integrity": "sha512-PRGGKCSKtyM3x629J9j4DMsH1cQT8UGW+R67u9Q5HrMK05gfjpmg+X1DQ3pgve4D8MI4R/Cm3NkYl2eUTbQHQg==",
"requires": {
"@aws-crypto/sha256-browser": "3.0.0",
"@aws-crypto/sha256-js": "3.0.0",
"@aws-sdk/config-resolver": "3.310.0",
"@aws-sdk/credential-provider-node": "3.310.0",
"@aws-sdk/credential-provider-node": "3.319.0",
"@aws-sdk/fetch-http-handler": "3.310.0",
"@aws-sdk/hash-node": "3.310.0",
"@aws-sdk/invalid-dependency": "3.310.0",
@ -13549,19 +13550,19 @@
"@aws-sdk/middleware-serde": "3.310.0",
"@aws-sdk/middleware-signing": "3.310.0",
"@aws-sdk/middleware-stack": "3.310.0",
"@aws-sdk/middleware-user-agent": "3.310.0",
"@aws-sdk/middleware-user-agent": "3.319.0",
"@aws-sdk/node-config-provider": "3.310.0",
"@aws-sdk/node-http-handler": "3.310.0",
"@aws-sdk/protocol-http": "3.310.0",
"@aws-sdk/smithy-client": "3.310.0",
"@aws-sdk/smithy-client": "3.316.0",
"@aws-sdk/types": "3.310.0",
"@aws-sdk/url-parser": "3.310.0",
"@aws-sdk/util-base64": "3.310.0",
"@aws-sdk/util-body-length-browser": "3.310.0",
"@aws-sdk/util-body-length-node": "3.310.0",
"@aws-sdk/util-defaults-mode-browser": "3.310.0",
"@aws-sdk/util-defaults-mode-node": "3.310.0",
"@aws-sdk/util-endpoints": "3.310.0",
"@aws-sdk/util-defaults-mode-browser": "3.316.0",
"@aws-sdk/util-defaults-mode-node": "3.316.0",
"@aws-sdk/util-endpoints": "3.319.0",
"@aws-sdk/util-retry": "3.310.0",
"@aws-sdk/util-user-agent-browser": "3.310.0",
"@aws-sdk/util-user-agent-node": "3.310.0",
@ -13604,14 +13605,14 @@
}
},
"@aws-sdk/credential-provider-ini": {
"version": "3.310.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.310.0.tgz",
"integrity": "sha512-gtRz7I+4BBpwZ3tc6UIt5lQuiAFnkpOibxHh95x1M6HDxBjm+uqD6RPZYVH+dULZPYXOtOTsHV0IGjrcV0sSRg==",
"version": "3.319.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.319.0.tgz",
"integrity": "sha512-pzx388Fw1KlSgmIMUyRY8DJVYM3aXpwzjprD4RiQVPJeAI+t7oQmEvd2FiUZEuHDjWXcuonxgU+dk7i7HUk/HQ==",
"requires": {
"@aws-sdk/credential-provider-env": "3.310.0",
"@aws-sdk/credential-provider-imds": "3.310.0",
"@aws-sdk/credential-provider-process": "3.310.0",
"@aws-sdk/credential-provider-sso": "3.310.0",
"@aws-sdk/credential-provider-sso": "3.319.0",
"@aws-sdk/credential-provider-web-identity": "3.310.0",
"@aws-sdk/property-provider": "3.310.0",
"@aws-sdk/shared-ini-file-loader": "3.310.0",
@ -13620,15 +13621,15 @@
}
},
"@aws-sdk/credential-provider-node": {
"version": "3.310.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.310.0.tgz",
"integrity": "sha512-FrOztUcOq2Sp32xGtJvxfvdlmuAeoxIu/AElHzV1bkx6Pzo9DkQBhXrSQ+JFSpI++weOD4ZGFhAvgbgUOT4VAg==",
"version": "3.319.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.319.0.tgz",
"integrity": "sha512-DS4a0Rdd7ZtMshoeE+zuSgbC05YBcdzd0h89u/eX+1Yqx+HCjeb8WXkbXsz0Mwx8q9TE04aS8f6Bw9J4x4mO5g==",
"requires": {
"@aws-sdk/credential-provider-env": "3.310.0",
"@aws-sdk/credential-provider-imds": "3.310.0",
"@aws-sdk/credential-provider-ini": "3.310.0",
"@aws-sdk/credential-provider-ini": "3.319.0",
"@aws-sdk/credential-provider-process": "3.310.0",
"@aws-sdk/credential-provider-sso": "3.310.0",
"@aws-sdk/credential-provider-sso": "3.319.0",
"@aws-sdk/credential-provider-web-identity": "3.310.0",
"@aws-sdk/property-provider": "3.310.0",
"@aws-sdk/shared-ini-file-loader": "3.310.0",
@ -13648,14 +13649,14 @@
}
},
"@aws-sdk/credential-provider-sso": {
"version": "3.310.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.310.0.tgz",
"integrity": "sha512-nXkpT8mrM/wRqSiz/a4p9U2UrOKyfZXhbPHIHyQj8K+uLjsYS+WPuH287J4A5Q57A6uarTrj5RjHmVeZVLaHmg==",
"version": "3.319.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.319.0.tgz",
"integrity": "sha512-gAUnWH41lxkIbANXu+Rz5zS0Iavjjmpf3C56vAMT7oaYZ3Cg/Ys5l2SwAucQGOCA2DdS2hDiSI8E+Yhr4F5toA==",
"requires": {
"@aws-sdk/client-sso": "3.310.0",
"@aws-sdk/client-sso": "3.319.0",
"@aws-sdk/property-provider": "3.310.0",
"@aws-sdk/shared-ini-file-loader": "3.310.0",
"@aws-sdk/token-providers": "3.310.0",
"@aws-sdk/token-providers": "3.319.0",
"@aws-sdk/types": "3.310.0",
"tslib": "^2.5.0"
}
@ -13816,13 +13817,13 @@
}
},
"@aws-sdk/middleware-user-agent": {
"version": "3.310.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.310.0.tgz",
"integrity": "sha512-x3IOwSwSbwKidlxRk3CNVHVUb06SRuaELxggCaR++QVI8NU6qD/l4VHXKVRvbTHiC/cYxXE/GaBDgQVpDR7V/g==",
"version": "3.319.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.319.0.tgz",
"integrity": "sha512-ytaLx2dlR5AdMSne6FuDCISVg8hjyKj+cHU20b2CRA/E/z+XXrLrssp4JrCgizRKPPUep0psMIa22Zd6osTT5Q==",
"requires": {
"@aws-sdk/protocol-http": "3.310.0",
"@aws-sdk/types": "3.310.0",
"@aws-sdk/util-endpoints": "3.310.0",
"@aws-sdk/util-endpoints": "3.319.0",
"tslib": "^2.5.0"
}
},
@ -13915,9 +13916,9 @@
}
},
"@aws-sdk/smithy-client": {
"version": "3.310.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.310.0.tgz",
"integrity": "sha512-UHMFvhoB2RLzsTb0mQe1ofvBUg/+/JEu1uptavxf/hEpEKZnRAaHH5FNkTG+mbFd/olay/QFjqNcMD6t8LcsNQ==",
"version": "3.316.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.316.0.tgz",
"integrity": "sha512-6YXOKbRnXeS8r8RWzuL6JMBolDYM5Wa4fD/VY6x/wK78i2xErHOvqzHgyyeLI1MMw4uqyd4wRNJNWC9TMPduXw==",
"requires": {
"@aws-sdk/middleware-stack": "3.310.0",
"@aws-sdk/types": "3.310.0",
@ -13925,11 +13926,11 @@
}
},
"@aws-sdk/token-providers": {
"version": "3.310.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.310.0.tgz",
"integrity": "sha512-G1JvB+2v8k900VJFkKVQXgLGF50ShOEIPxfK1gSQLkSU85vPwGIAANs1KvnlW08FsNbWp3+sKca4kfYKsooXMw==",
"version": "3.319.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.319.0.tgz",
"integrity": "sha512-5utg6VL6Pl0uiLUn8ZJPYYxzCb9VRPsgJmGXktRUwq0YlTJ6ABcaxTXwZcC++sjh/qyCQDK5PPLNU5kIBttHMQ==",
"requires": {
"@aws-sdk/client-sso-oidc": "3.310.0",
"@aws-sdk/client-sso-oidc": "3.319.0",
"@aws-sdk/property-provider": "3.310.0",
"@aws-sdk/shared-ini-file-loader": "3.310.0",
"@aws-sdk/types": "3.310.0",
@ -13997,9 +13998,9 @@
}
},
"@aws-sdk/util-defaults-mode-browser": {
"version": "3.310.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.310.0.tgz",
"integrity": "sha512-Mr2AoQsjAYNM5oAS2YJlYJqhiCvkFV/hu48slOZgbY4G7ueW4cM0DPkR16wqjcRCGqZ4JmAZB8Q5R0DMrLjhOQ==",
"version": "3.316.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.316.0.tgz",
"integrity": "sha512-6FSqLhYmaihtH2n1s4b2rlLW0ABU8N6VZIfzLfe2ING4PF0MzfaMMhnTFUHVXfKCVGoR8yP6iyFTRCyHGVEL1w==",
"requires": {
"@aws-sdk/property-provider": "3.310.0",
"@aws-sdk/types": "3.310.0",
@ -14008,9 +14009,9 @@
}
},
"@aws-sdk/util-defaults-mode-node": {
"version": "3.310.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.310.0.tgz",
"integrity": "sha512-JyBlvhQGR8w8NpFRZZXRVTDesafFKTu/gTWjcoxP7twa+fYHSIgPPFGnlcJ/iHaucjamSaWi5EQ+YQmnSZ8yHA==",
"version": "3.316.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.316.0.tgz",
"integrity": "sha512-dkYy10hdjPSScXXvnjGpZpnJxllkb6ICHgLMwZ4JczLHhPM12T/4PQ758YN8HS+muiYDGX1Bl2z1jd/bMcewBQ==",
"requires": {
"@aws-sdk/config-resolver": "3.310.0",
"@aws-sdk/credential-provider-imds": "3.310.0",
@ -14021,9 +14022,9 @@
}
},
"@aws-sdk/util-endpoints": {
"version": "3.310.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.310.0.tgz",
"integrity": "sha512-zG+/d/O5KPmAaeOMPd6bW1abifdT0H03f42keLjYEoRZzYtHPC5DuPE0UayiWGckI6BCDgy0sRKXCYS49UNFaQ==",
"version": "3.319.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.319.0.tgz",
"integrity": "sha512-3I64UMoYA2e2++oOUJXRcFtYLpLylnZFRltWfPo1B3dLlf+MIWat9djT+mMus+hW1ntLsvAIVu1hLVePJC0gvw==",
"requires": {
"@aws-sdk/types": "3.310.0",
"tslib": "^2.5.0"
@ -16063,37 +16064,37 @@
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
},
"@sentry-internal/tracing": {
"version": "7.47.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.47.0.tgz",
"integrity": "sha512-udpHnCzF8DQsWf0gQwd0XFGp6Y8MOiwnl8vGt2ohqZGS3m1+IxoRLXsSkD8qmvN6KKDnwbaAvYnK0z0L+AW95g==",
"version": "7.49.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.49.0.tgz",
"integrity": "sha512-ESh3+ZneQk/3HESTUmIPNrW5GVPu/HrRJU+eAJJto74vm+6vP7zDn2YV2gJ1w18O/37nc7W/bVCgZJlhZ3cwew==",
"requires": {
"@sentry/core": "7.47.0",
"@sentry/types": "7.47.0",
"@sentry/utils": "7.47.0",
"@sentry/core": "7.49.0",
"@sentry/types": "7.49.0",
"@sentry/utils": "7.49.0",
"tslib": "^1.9.3"
},
"dependencies": {
"@sentry/core": {
"version": "7.47.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.47.0.tgz",
"integrity": "sha512-EFhZhKdMu7wKmWYZwbgTi8FNZ7Fq+HdlXiZWNz51Bqe3pHmfAkdHtAEs0Buo0v623MKA0CA4EjXIazGUM34XTg==",
"version": "7.49.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.49.0.tgz",
"integrity": "sha512-AlSnCYgfEbvK8pkNluUkmdW/cD9UpvOVCa+ERQswXNRkAv5aDGCL6Ihv6fnIajE++BYuwZh0+HwZUBVKTFzoZg==",
"requires": {
"@sentry/types": "7.47.0",
"@sentry/utils": "7.47.0",
"@sentry/types": "7.49.0",
"@sentry/utils": "7.49.0",
"tslib": "^1.9.3"
}
},
"@sentry/types": {
"version": "7.47.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.47.0.tgz",
"integrity": "sha512-GxXocplN0j1+uczovHrfkykl9wvkamDtWxlPUQgyGlbLGZn+UH1Y79D4D58COaFWGEZdSNKr62gZAjfEYu9nQA=="
"version": "7.49.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.49.0.tgz",
"integrity": "sha512-9yXXh7iv76+O6h2ONUVx0wsL1auqJFWez62mTjWk4350SgMmWp/zUkBxnVXhmcYqscz/CepC+Loz9vITLXtgxg=="
},
"@sentry/utils": {
"version": "7.47.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.47.0.tgz",
"integrity": "sha512-A89SaOLp6XeZfByeYo2C8Ecye/YAtk/gENuyOUhQEdMulI6mZdjqtHAp7pTMVgkBc/YNARVuoa+kR/IdRrTPkQ==",
"version": "7.49.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.49.0.tgz",
"integrity": "sha512-JdC9yGnOgev4ISJVwmIoFsk8Zx0psDZJAj2DV7x4wMZsO6QK+YjC7G3mUED/S5D5lsrkBZ/3uvQQhr8DQI4UcQ==",
"requires": {
"@sentry/types": "7.47.0",
"@sentry/types": "7.49.0",
"tslib": "^1.9.3"
}
},
@ -16122,14 +16123,14 @@
}
},
"@sentry/node": {
"version": "7.47.0",
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.47.0.tgz",
"integrity": "sha512-LTg2r5EV9yh4GLYDF+ViSltR9LLj/pcvk8YhANJcMO3Fp//xh8njcdU0FC2yNthUREawYDzAsVzLyCYJfV0H1A==",
"version": "7.49.0",
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.49.0.tgz",
"integrity": "sha512-KLIrqcbKk4yR3g8fjl87Eyv4M9j4YI6b7sqVAZYj3FrX3mC6JQyGdlDfUpSKy604n1iAdr6OuUp5f9x7jPJaeQ==",
"requires": {
"@sentry-internal/tracing": "7.47.0",
"@sentry/core": "7.47.0",
"@sentry/types": "7.47.0",
"@sentry/utils": "7.47.0",
"@sentry-internal/tracing": "7.49.0",
"@sentry/core": "7.49.0",
"@sentry/types": "7.49.0",
"@sentry/utils": "7.49.0",
"cookie": "^0.4.1",
"https-proxy-agent": "^5.0.0",
"lru_map": "^0.3.3",
@ -16137,26 +16138,26 @@
},
"dependencies": {
"@sentry/core": {
"version": "7.47.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.47.0.tgz",
"integrity": "sha512-EFhZhKdMu7wKmWYZwbgTi8FNZ7Fq+HdlXiZWNz51Bqe3pHmfAkdHtAEs0Buo0v623MKA0CA4EjXIazGUM34XTg==",
"version": "7.49.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.49.0.tgz",
"integrity": "sha512-AlSnCYgfEbvK8pkNluUkmdW/cD9UpvOVCa+ERQswXNRkAv5aDGCL6Ihv6fnIajE++BYuwZh0+HwZUBVKTFzoZg==",
"requires": {
"@sentry/types": "7.47.0",
"@sentry/utils": "7.47.0",
"@sentry/types": "7.49.0",
"@sentry/utils": "7.49.0",
"tslib": "^1.9.3"
}
},
"@sentry/types": {
"version": "7.47.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.47.0.tgz",
"integrity": "sha512-GxXocplN0j1+uczovHrfkykl9wvkamDtWxlPUQgyGlbLGZn+UH1Y79D4D58COaFWGEZdSNKr62gZAjfEYu9nQA=="
"version": "7.49.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.49.0.tgz",
"integrity": "sha512-9yXXh7iv76+O6h2ONUVx0wsL1auqJFWez62mTjWk4350SgMmWp/zUkBxnVXhmcYqscz/CepC+Loz9vITLXtgxg=="
},
"@sentry/utils": {
"version": "7.47.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.47.0.tgz",
"integrity": "sha512-A89SaOLp6XeZfByeYo2C8Ecye/YAtk/gENuyOUhQEdMulI6mZdjqtHAp7pTMVgkBc/YNARVuoa+kR/IdRrTPkQ==",
"version": "7.49.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.49.0.tgz",
"integrity": "sha512-JdC9yGnOgev4ISJVwmIoFsk8Zx0psDZJAj2DV7x4wMZsO6QK+YjC7G3mUED/S5D5lsrkBZ/3uvQQhr8DQI4UcQ==",
"requires": {
"@sentry/types": "7.47.0",
"@sentry/types": "7.49.0",
"tslib": "^1.9.3"
}
},
@ -16973,9 +16974,9 @@
"integrity": "sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g=="
},
"aws-sdk": {
"version": "2.1360.0",
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1360.0.tgz",
"integrity": "sha512-wW1CviH1s6bl5+wO+KM7aSc3yy6cQPJT85Fd4rQgrn0uwfjg9fx7KJ0FRhv+eU4DabkRjcSMlKo1IGhARmT6Tw==",
"version": "2.1364.0",
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1364.0.tgz",
"integrity": "sha512-PFoq9Rnu0DVi07wZ/SjKlrJDwso8AxE5q/ufLdNtINZPaSkB92OqKYVdlfQ4srsH2ala2NCrg8gFX98SCmqW3w==",
"requires": {
"buffer": "4.9.2",
"events": "1.1.1",

View File

@ -1,15 +1,15 @@
{
"dependencies": {
"@aws-sdk/client-secrets-manager": "^3.312.0",
"@aws-sdk/client-secrets-manager": "^3.319.0",
"@godaddy/terminus": "^4.12.0",
"@octokit/rest": "^19.0.5",
"@sentry/node": "^7.41.0",
"@sentry/node": "^7.49.0",
"@sentry/tracing": "^7.48.0",
"@types/crypto-js": "^4.1.1",
"@types/libsodium-wrappers": "^0.7.10",
"argon2": "^0.30.3",
"await-to-js": "^3.0.0",
"aws-sdk": "^2.1360.0",
"aws-sdk": "^2.1364.0",
"axios": "^1.3.5",
"axios-retry": "^3.4.0",
"bcrypt": "^5.1.0",
@ -31,6 +31,7 @@
"libsodium-wrappers": "^0.7.10",
"lodash": "^4.17.21",
"mongoose": "^6.10.5",
"node-cache": "^5.1.2",
"nodemailer": "^6.8.0",
"posthog-node": "^2.6.0",
"query-string": "^7.1.3",

View File

@ -5,7 +5,7 @@ const client = new InfisicalClient({
});
export const getPort = async () => (await client.getSecret('PORT')).secretValue || 4000;
export const getInviteOnlySignup = async () => (await client.getSecret('INVITE_ONLY_SIGNUP')).secretValue == undefined ? false : (await client.getSecret('INVITE_ONLY_SIGNUP')).secretValue;
export const getInviteOnlySignup = async () => (await client.getSecret('INVITE_ONLY_SIGNUP')).secretValue === 'true'
export const getEncryptionKey = async () => (await client.getSecret('ENCRYPTION_KEY')).secretValue;
export const getSaltRounds = async () => parseInt((await client.getSecret('SALT_ROUNDS')).secretValue) || 10;
export const getJwtAuthLifetime = async () => (await client.getSecret('JWT_AUTH_LIFETIME')).secretValue || '10d';
@ -45,12 +45,19 @@ export const getSmtpUsername = async () => (await client.getSecret('SMTP_USERNAM
export const getSmtpPassword = async () => (await client.getSecret('SMTP_PASSWORD')).secretValue;
export const getSmtpFromAddress = async () => (await client.getSecret('SMTP_FROM_ADDRESS')).secretValue;
export const getSmtpFromName = async () => (await client.getSecret('SMTP_FROM_NAME')).secretValue || 'Infisical';
export const getLicenseKey = async () => (await client.getSecret('LICENSE_KEY')).secretValue;
export const getLicenseServerKey = async () => (await client.getSecret('LICENSE_SERVER_KEY')).secretValue;
export const getLicenseServerUrl = async () => (await client.getSecret('LICENSE_SERVER_URL')).secretValue || 'https://portal.infisical.com';
// TODO: deprecate from here
export const getStripeProductStarter = async () => (await client.getSecret('STRIPE_PRODUCT_STARTER')).secretValue;
export const getStripeProductPro = async () => (await client.getSecret('STRIPE_PRODUCT_PRO')).secretValue;
export const getStripeProductTeam = async () => (await client.getSecret('STRIPE_PRODUCT_TEAM')).secretValue;
export const getStripePublishableKey = async () => (await client.getSecret('STRIPE_PUBLISHABLE_KEY')).secretValue;
export const getStripeSecretKey = async () => (await client.getSecret('STRIPE_SECRET_KEY')).secretValue;
export const getStripeWebhookSecret = async () => (await client.getSecret('STRIPE_WEBHOOK_SECRET')).secretValue;
export const getTelemetryEnabled = async () => (await client.getSecret('TELEMETRY_ENABLED')).secretValue !== 'false' && true;
export const getLoopsApiKey = async () => (await client.getSecret('LOOPS_API_KEY')).secretValue;
export const getSmtpConfigured = async () => (await client.getSecret('SMTP_HOST')).secretValue == '' || (await client.getSecret('SMTP_HOST')).secretValue == undefined ? false : true

View File

@ -1,10 +1,24 @@
import axios from 'axios';
import axiosRetry from 'axios-retry';
import {
getLicenseServerKeyAuthToken,
setLicenseServerKeyAuthToken,
getLicenseKeyAuthToken,
setLicenseKeyAuthToken
} from './storage';
import {
getLicenseKey,
getLicenseServerKey,
getLicenseServerUrl
} from './index';
const axiosInstance = axios.create();
// should have JWT to interact with the license server
export const licenseServerKeyRequest = axios.create();
export const licenseKeyRequest = axios.create();
export const standardRequest = axios.create();
// add retry functionality to the axios instance
axiosRetry(axiosInstance, {
axiosRetry(standardRequest, {
retries: 3,
retryDelay: axiosRetry.exponentialDelay, // exponential back-off delay between retries
retryCondition: (error) => {
@ -13,4 +27,98 @@ axiosRetry(axiosInstance, {
},
});
export default axiosInstance;
export const refreshLicenseServerKeyToken = async () => {
const licenseServerKey = await getLicenseServerKey();
const licenseServerUrl = await getLicenseServerUrl();
const { data: { token } } = await standardRequest.post(
`${licenseServerUrl}/api/auth/v1/license-server-login`, {},
{
headers: {
'X-API-KEY': licenseServerKey
}
}
);
setLicenseServerKeyAuthToken(token);
return token;
}
export const refreshLicenseKeyToken = async () => {
const licenseKey = await getLicenseKey();
const licenseServerUrl = await getLicenseServerUrl();
const { data: { token } } = await standardRequest.post(
`${licenseServerUrl}/api/auth/v1/license-login`, {},
{
headers: {
'X-API-KEY': licenseKey
}
}
);
setLicenseKeyAuthToken(token);
return token;
}
licenseServerKeyRequest.interceptors.request.use((config) => {
const token = getLicenseServerKeyAuthToken();
if (token && config.headers) {
// eslint-disable-next-line no-param-reassign
config.headers.Authorization = `Bearer ${token}`;
}
return config;
}, (err) => {
return Promise.reject(err);
});
licenseServerKeyRequest.interceptors.response.use((response) => {
return response
}, async function (err) {
const originalRequest = err.config;
if (err.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
// refresh
const token = await refreshLicenseServerKeyToken();
axios.defaults.headers.common['Authorization'] = 'Bearer ' + token;
return licenseServerKeyRequest(originalRequest);
}
return Promise.reject(err);
});
licenseKeyRequest.interceptors.request.use((config) => {
const token = getLicenseKeyAuthToken();
if (token && config.headers) {
// eslint-disable-next-line no-param-reassign
config.headers.Authorization = `Bearer ${token}`;
}
return config;
}, (err) => {
return Promise.reject(err);
});
licenseKeyRequest.interceptors.response.use((response) => {
return response
}, async function (err) {
const originalRequest = err.config;
if (err.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
// refresh
const token = await refreshLicenseKeyToken();
axios.defaults.headers.common['Authorization'] = 'Bearer ' + token;
return licenseKeyRequest(originalRequest);
}
return Promise.reject(err);
});

View File

@ -0,0 +1,30 @@
const MemoryLicenseServerKeyTokenStorage = () => {
let authToken: string;
return {
setToken: (token: string) => {
authToken = token;
},
getToken: () => authToken
};
};
const MemoryLicenseKeyTokenStorage = () => {
let authToken: string;
return {
setToken: (token: string) => {
authToken = token;
},
getToken: () => authToken
};
};
const licenseServerTokenStorage = MemoryLicenseServerKeyTokenStorage();
const licenseTokenStorage = MemoryLicenseKeyTokenStorage();
export const getLicenseServerKeyAuthToken = licenseServerTokenStorage.getToken;
export const setLicenseServerKeyAuthToken = licenseServerTokenStorage.setToken;
export const getLicenseKeyAuthToken = licenseTokenStorage.getToken;
export const setLicenseKeyAuthToken = licenseTokenStorage.setToken;

View File

@ -16,7 +16,7 @@ import {
INTEGRATION_VERCEL_API_URL,
INTEGRATION_RAILWAY_API_URL
} from '../../variables';
import request from '../../config/request';
import { standardRequest } from '../../config/request';
/***
* Return integration authorization with id [integrationAuthId]
@ -229,7 +229,7 @@ export const getIntegrationAuthVercelBranches = async (req: Request, res: Respon
let branches: string[] = [];
if (appId && appId !== '') {
const { data }: { data: VercelBranch[] } = await request.get(
const { data }: { data: VercelBranch[] } = await standardRequest.get(
`${INTEGRATION_VERCEL_API_URL}/v1/integrations/git-branches`,
{
params,
@ -292,7 +292,7 @@ export const getIntegrationAuthRailwayEnvironments = async (req: Request, res: R
projectId: appId
}
const { data: { data: { environments: { edges } } } } = await request.post(INTEGRATION_RAILWAY_API_URL, {
const { data: { data: { environments: { edges } } } } = await standardRequest.post(INTEGRATION_RAILWAY_API_URL, {
query,
variables,
}, {
@ -372,7 +372,7 @@ export const getIntegrationAuthRailwayServices = async (req: Request, res: Respo
id: appId
}
const { data: { data: { project: { services: { edges } } } } } = await request.post(INTEGRATION_RAILWAY_API_URL, {
const { data: { data: { project: { services: { edges } } } } } = await standardRequest.post(INTEGRATION_RAILWAY_API_URL, {
query,
variables
}, {

View File

@ -135,6 +135,7 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
}
if (!inviteeMembershipOrg) {
await new MembershipOrg({
user: invitee,
inviteEmail: inviteeEmail,
@ -246,6 +247,10 @@ export const verifyUserToOrganization = async (req: Request, res: Response) => {
// membership can be approved and redirected to login/dashboard
membershipOrg.status = ACCEPTED;
await membershipOrg.save();
await updateSubscriptionOrgQuantity({
organizationId
});
return res.status(200).send({
message: 'Successfully verified email',

View File

@ -19,7 +19,8 @@ export const getOrganizations = async (req: Request, res: Response) => {
try {
organizations = (
await MembershipOrg.find({
user: req.user._id
user: req.user._id,
status: ACCEPTED
}).populate('organization')
).map((m) => m.organization);
} catch (err) {

View File

@ -21,14 +21,6 @@ export const beginEmailSignup = async (req: Request, res: Response) => {
try {
email = req.body.email;
if (await getInviteOnlySignup()) {
// Only one user can create an account without being invited. The rest need to be invited in order to make an account
const userCount = await User.countDocuments({})
if (userCount != 0) {
throw BadRequestError({ message: "New user sign ups are not allowed at this time. You must be invited to sign up." })
}
}
const user = await User.findOne({ email }).select('+publicKey');
if (user && user?.publicKey) {
// case: user has already completed account
@ -74,6 +66,14 @@ export const verifyEmailSignup = async (req: Request, res: Response) => {
});
}
if (await getInviteOnlySignup()) {
// Only one user can create an account without being invited. The rest need to be invited in order to make an account
const userCount = await User.countDocuments({})
if (userCount != 0) {
throw BadRequestError({ message: "New user sign ups are not allowed at this time. You must be invited to sign up." })
}
}
// verify email
if (await getSmtpConfigured()) {
await checkEmailVerification({

View File

@ -1,26 +1,24 @@
import to from 'await-to-js';
import { Types } from 'mongoose';
import { Request, Response } from 'express';
import { ISecret, Secret } from '../../models';
import { ISecret, Secret, Workspace } from '../../models';
import { IAction, SecretVersion } from '../../ee/models';
import {
SECRET_PERSONAL,
SECRET_SHARED,
ACTION_ADD_SECRETS,
ACTION_READ_SECRETS,
ACTION_UPDATE_SECRETS,
ACTION_DELETE_SECRETS
} from '../../variables';
import { UnauthorizedRequestError, ValidationError } from '../../utils/errors';
import { UnauthorizedRequestError, WorkspaceNotFoundError } from '../../utils/errors';
import { EventService } from '../../services';
import { eventPushSecrets } from '../../events';
import { EESecretService, EELogService } from '../../ee/services';
import { EESecretService, EELogService, EELicenseService } from '../../ee/services';
import { TelemetryService, SecretService } from '../../services';
import { getChannelFromUserAgent } from '../../utils/posthog';
import { PERMISSION_WRITE_SECRETS } from '../../variables';
import { userHasNoAbility, userHasWorkspaceAccess, userHasWriteOnlyAbility } from '../../ee/helpers/checkMembershipPermissions';
import Tag from '../../models/tag';
import _, { eq } from 'lodash';
import _ from 'lodash';
import {
BatchSecretRequest,
BatchSecret
@ -48,6 +46,12 @@ export const batchSecrets = async (req: Request, res: Response) => {
environment: string;
requests: BatchSecretRequest[];
} = req.body;
const workspace = await Workspace.findById(workspaceId);
if (!workspace) throw WorkspaceNotFoundError();
const orgPlan = await EELicenseService.getOrganizationPlan(workspace.organization.toString());
const isPaid = orgPlan.tier < 1;
const createSecrets: BatchSecret[] = [];
const updateSecrets: BatchSecret[] = [];
@ -139,7 +143,8 @@ export const batchSecrets = async (req: Request, res: Response) => {
environment,
workspaceId,
channel,
userAgent: req.headers?.['user-agent']
userAgent: req.headers?.['user-agent'],
isPaid
}
});
}
@ -226,7 +231,8 @@ export const batchSecrets = async (req: Request, res: Response) => {
environment,
workspaceId,
channel,
userAgent: req.headers?.['user-agent']
userAgent: req.headers?.['user-agent'],
isPaid
}
});
}
@ -261,7 +267,8 @@ export const batchSecrets = async (req: Request, res: Response) => {
environment,
workspaceId,
channel: channel,
userAgent: req.headers?.['user-agent']
userAgent: req.headers?.['user-agent'],
isPaid
}
});
}
@ -376,6 +383,12 @@ export const createSecrets = async (req: Request, res: Response) => {
}
}
const workspace = await Workspace.findById(workspaceId);
if (!workspace) throw WorkspaceNotFoundError();
const orgPlan = await EELicenseService.getOrganizationPlan(workspace.organization.toString());
const isPaid = orgPlan.tier < 1;
let listOfSecretsToCreate;
if (Array.isArray(req.body.secrets)) {
// case: create multiple secrets
@ -531,7 +544,8 @@ export const createSecrets = async (req: Request, res: Response) => {
environment,
workspaceId,
channel: channel,
userAgent: req.headers?.['user-agent']
userAgent: req.headers?.['user-agent'],
isPaid
}
});
}
@ -595,6 +609,12 @@ export const getSecrets = async (req: Request, res: Response) => {
const normalizedPath = normalizePath(secretsPath as string)
const folders = await getFoldersInDirectory(workspaceId as string, environment as string, normalizedPath)
const workspace = await Workspace.findById(workspaceId);
if (!workspace) throw WorkspaceNotFoundError();
const orgPlan = await EELicenseService.getOrganizationPlan(workspace.organization.toString());
const isPaid = orgPlan.tier < 1;
// secrets to return
let secrets: ISecret[] = [];
@ -727,7 +747,8 @@ export const getSecrets = async (req: Request, res: Response) => {
environment,
workspaceId,
channel,
userAgent: req.headers?.['user-agent']
userAgent: req.headers?.['user-agent'],
isPaid
}
});
}
@ -938,6 +959,12 @@ export const updateSecrets = async (req: Request, res: Response) => {
workspaceId: new Types.ObjectId(key)
})
const workspace = await Workspace.findById(key);
if (!workspace) throw WorkspaceNotFoundError();
const orgPlan = await EELicenseService.getOrganizationPlan(workspace.organization.toString());
const isPaid = orgPlan.tier < 1;
const postHogClient = await TelemetryService.getPostHogClient();
if (postHogClient) {
postHogClient.capture({
@ -950,7 +977,8 @@ export const updateSecrets = async (req: Request, res: Response) => {
environment: workspaceSecretObj[key][0].environment,
workspaceId: key,
channel: channel,
userAgent: req.headers?.['user-agent']
userAgent: req.headers?.['user-agent'],
isPaid
}
});
}
@ -1072,6 +1100,14 @@ export const deleteSecrets = async (req: Request, res: Response) => {
workspaceId: new Types.ObjectId(key)
});
const organizationId = (
await Workspace.findOne({
_id: key
})
)?.organization?.toString();
const orgPlan = await EELicenseService.getOrganizationPlan(organizationId || '');
const isPaid = orgPlan.slug != 'starter';
const postHogClient = await TelemetryService.getPostHogClient();
if (postHogClient) {
postHogClient.capture({
@ -1084,7 +1120,8 @@ export const deleteSecrets = async (req: Request, res: Response) => {
environment: workspaceSecretObj[key][0].environment,
workspaceId: key,
channel: channel,
userAgent: req.headers?.['user-agent']
userAgent: req.headers?.['user-agent'],
isPaid
}
});
}

View File

@ -7,8 +7,9 @@ import {
} from '../../helpers/signup';
import { issueAuthTokens } from '../../helpers/auth';
import { INVITED, ACCEPTED } from '../../variables';
import request from '../../config/request';
import { standardRequest } from '../../config/request';
import { getLoopsApiKey, getHttpsEnabled } from '../../config';
import { updateSubscriptionOrgQuantity } from '../../helpers/organization';
/**
* Complete setting up user by adding their personal and auth information as part of the
@ -87,6 +88,19 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
user
});
// update organization membership statuses that are
// invited to completed with user attached
const membershipsToUpdate = await MembershipOrg.find({
inviteEmail: email,
status: INVITED
});
membershipsToUpdate.forEach(async (membership) => {
await updateSubscriptionOrgQuantity({
organizationId: membership.organization.toString()
});
});
// update organization membership statuses that are
// invited to completed with user attached
await MembershipOrg.updateMany(
@ -109,7 +123,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
// sending a welcome email to new users
if (await getLoopsApiKey()) {
await request.post("https://app.loops.so/api/v1/events/send", {
await standardRequest.post("https://app.loops.so/api/v1/events/send", {
"email": email,
"eventName": "Sign Up",
"firstName": firstName,
@ -206,9 +220,20 @@ export const completeAccountInvite = async (req: Request, res: Response) => {
if (!user)
throw new Error('Failed to complete account for non-existent user');
// update organization membership statuses that are
// invited to completed with user attached
const membershipsToUpdate = await MembershipOrg.find({
inviteEmail: email,
status: INVITED
});
membershipsToUpdate.forEach(async (membership) => {
await updateSubscriptionOrgQuantity({
organizationId: membership.organization.toString()
});
});
await MembershipOrg.updateMany(
{
inviteEmail: email,

View File

@ -0,0 +1,34 @@
import * as Sentry from '@sentry/node';
import { Request, Response } from 'express';
import { EELicenseService } from '../../services';
import { getLicenseServerUrl } from '../../../config';
import { licenseServerKeyRequest } from '../../../config/request';
/**
* Return available cloud product information.
* Note: Nicely formatted to easily construct a table from
* @param req
* @param res
* @returns
*/
export const getCloudProducts = async (req: Request, res: Response) => {
try {
const billingCycle = req.query['billing-cycle'] as string;
if (EELicenseService.instanceType === 'cloud') {
const { data } = await licenseServerKeyRequest.get(
`${await getLicenseServerUrl()}/api/license-server/v1/cloud-products?billing-cycle=${billingCycle}`
);
return res.status(200).send(data);
}
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
}
return res.status(200).send({
head: [],
rows: []
});
}

View File

@ -1,15 +1,19 @@
import * as stripeController from './stripeController';
import * as secretController from './secretController';
import * as secretSnapshotController from './secretSnapshotController';
import * as organizationsController from './organizationsController';
import * as workspaceController from './workspaceController';
import * as actionController from './actionController';
import * as membershipController from './membershipController';
import * as cloudProductsController from './cloudProductsController';
export {
stripeController,
secretController,
secretSnapshotController,
organizationsController,
workspaceController,
actionController,
membershipController
membershipController,
cloudProductsController
}

View File

@ -0,0 +1,84 @@
import { Request, Response } from 'express';
import { getLicenseServerUrl } from '../../../config';
import { licenseServerKeyRequest } from '../../../config/request';
import { EELicenseService } from '../../services';
/**
* Return the organization's current plan and allowed feature set
*/
export const getOrganizationPlan = async (req: Request, res: Response) => {
const plan = await EELicenseService.getOrganizationPlan(req.organization._id.toString());
// cache fetched plan for organization
EELicenseService.localFeatureSet.set(req.organization._id.toString(), plan);
return res.status(200).send({
plan
});
}
/**
* Update the organization plan to product with id [productId]
* @param req
* @param res
* @returns
*/
export const updateOrganizationPlan = async (req: Request, res: Response) => {
const {
productId
} = req.body;
const { data } = await licenseServerKeyRequest.patch(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/cloud-plan`,
{
productId
}
);
return res.status(200).send(data);
}
/**
* Return the organization's payment methods on file
*/
export const getOrganizationPmtMethods = async (req: Request, res: Response) => {
const { data: { pmtMethods } } = await licenseServerKeyRequest.get(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details/payment-methods`
);
return res.status(200).send({
pmtMethods
});
}
/**
* Return a Stripe session URL to add payment method for organization
*/
export const addOrganizationPmtMethod = async (req: Request, res: Response) => {
const {
success_url,
cancel_url
} = req.body;
const { data: { url } } = await licenseServerKeyRequest.post(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details/payment-methods`,
{
success_url,
cancel_url
}
);
return res.status(200).send({
url
});
}
export const deleteOrganizationPmtMethod = async (req: Request, res: Response) => {
const { pmtMethodId } = req.params;
const { data } = await licenseServerKeyRequest.delete(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details/payment-methods/${pmtMethodId}`,
);
return res.status(200).send(data);
}

View File

@ -0,0 +1,20 @@
import express from 'express';
const router = express.Router();
import {
requireAuth,
validateRequest
} from '../../../middleware';
import { query } from 'express-validator';
import { cloudProductsController } from '../../controllers/v1';
router.get(
'/',
requireAuth({
acceptedAuthModes: ['jwt', 'apiKey']
}),
query('billing-cycle').exists().isIn(['monthly', 'yearly']),
validateRequest,
cloudProductsController.getCloudProducts
);
export default router;

View File

@ -1,11 +1,15 @@
import secret from './secret';
import secretSnapshot from './secretSnapshot';
import organizations from './organizations';
import workspace from './workspace';
import action from './action';
import cloudProducts from './cloudProducts';
export {
secret,
secretSnapshot,
organizations,
workspace,
action
action,
cloudProducts
}

View File

@ -0,0 +1,87 @@
import express from 'express';
const router = express.Router();
import {
requireAuth,
requireOrganizationAuth,
validateRequest
} from '../../../middleware';
import { param, body } from 'express-validator';
import { organizationsController } from '../../controllers/v1';
import {
OWNER, ADMIN, MEMBER, ACCEPTED
} from '../../../variables';
router.get(
'/:organizationId/plan',
requireAuth({
acceptedAuthModes: ['jwt', 'apiKey']
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED]
}),
param('organizationId').exists().trim(),
validateRequest,
organizationsController.getOrganizationPlan
);
router.patch(
'/:organizationId/plan',
requireAuth({
acceptedAuthModes: ['jwt', 'apiKey']
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED]
}),
param('organizationId').exists().trim(),
body('productId').exists().isString(),
validateRequest,
organizationsController.updateOrganizationPlan
);
router.get(
'/:organizationId/billing-details/payment-methods',
requireAuth({
acceptedAuthModes: ['jwt', 'apiKey']
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED]
}),
param('organizationId').exists().trim(),
validateRequest,
organizationsController.getOrganizationPmtMethods
);
router.post(
'/:organizationId/billing-details/payment-methods',
requireAuth({
acceptedAuthModes: ['jwt', 'apiKey']
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED]
}),
param('organizationId').exists().trim(),
body('success_url').exists().isString(),
body('cancel_url').exists().isString(),
validateRequest,
organizationsController.addOrganizationPmtMethod
);
router.delete(
'/:organizationId/billing-details/payment-methods/:pmtMethodId',
requireAuth({
acceptedAuthModes: ['jwt', 'apiKey']
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED]
}),
param('organizationId').exists().trim(),
validateRequest,
organizationsController.deleteOrganizationPmtMethod
);
export default router;

View File

@ -7,7 +7,7 @@ import {
requireAuth,
validateRequest
} from '../../../middleware';
import { param, body } from 'express-validator';
import { param } from 'express-validator';
import { ADMIN, MEMBER } from '../../../variables';
import { secretSnapshotController } from '../../controllers/v1';

View File

@ -1,12 +1,124 @@
import NodeCache from 'node-cache';
import * as Sentry from '@sentry/node';
import {
getLicenseKey,
getLicenseServerKey,
getLicenseServerUrl
} from '../../config';
import {
licenseKeyRequest,
licenseServerKeyRequest,
refreshLicenseServerKeyToken,
refreshLicenseKeyToken
} from '../../config/request';
import { Organization } from '../../models';
import { OrganizationNotFoundError } from '../../utils/errors';
interface FeatureSet {
_id: string | null;
slug: 'starter' | 'team' | 'pro' | 'enterprise' | null;
tier: number | null;
projectLimit: number | null;
memberLimit: number | null;
secretVersioning: boolean;
pitRecovery: boolean;
rbac: boolean;
customRateLimits: boolean;
customAlerts: boolean;
auditLogs: boolean;
}
/**
* Class to handle Enterprise Edition license actions
* Class to handle license/plan configurations:
* - Infisical Cloud: Fetch and cache customer plans in [localFeatureSet]
* - Self-hosted regular: Use default global feature set
* - Self-hosted enterprise: Fetch and update global feature set
*/
class EELicenseService {
private readonly _isLicenseValid: boolean;
private readonly _isLicenseValid: boolean; // TODO: deprecate
public instanceType: 'self-hosted' | 'enterprise-self-hosted' | 'cloud' = 'self-hosted';
public globalFeatureSet: FeatureSet = {
_id: null,
slug: null,
tier: -1,
projectLimit: null,
memberLimit: null,
secretVersioning: true,
pitRecovery: true,
rbac: true,
customRateLimits: true,
customAlerts: true,
auditLogs: false
}
public localFeatureSet: NodeCache;
constructor(licenseKey: string) {
constructor() {
this._isLicenseValid = true;
this.localFeatureSet = new NodeCache({
stdTTL: 300
});
}
public async getOrganizationPlan(organizationId: string) {
try {
if (this.instanceType === 'cloud') {
const cachedPlan = this.localFeatureSet.get(organizationId);
if (cachedPlan) return cachedPlan;
const organization = await Organization.findById(organizationId);
if (!organization) throw OrganizationNotFoundError();
const { data: { currentPlan } } = await licenseServerKeyRequest.get(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${organization.customerId}/cloud-plan`
);
return currentPlan;
}
} catch (err) {
return this.globalFeatureSet;
}
return this.globalFeatureSet;
}
public async initGlobalFeatureSet() {
const licenseServerKey = await getLicenseServerKey();
const licenseKey = await getLicenseKey();
try {
if (licenseServerKey) {
// license server key is present -> validate it
const token = await refreshLicenseServerKeyToken()
if (token) {
this.instanceType = 'cloud';
}
return;
}
if (licenseKey) {
// license key is present -> validate it
const token = await refreshLicenseKeyToken();
if (token) {
const { data: { currentPlan } } = await licenseKeyRequest.get(
`${await getLicenseServerUrl()}/api/license/v1/plan`
);
this.globalFeatureSet = currentPlan;
this.instanceType = 'enterprise-self-hosted';
}
}
} catch (err) {
// case: self-hosted free
Sentry.setUser(null);
Sentry.captureException(err);
}
}
public get isLicenseValid(): boolean {
@ -14,4 +126,4 @@ class EELicenseService {
}
}
export default new EELicenseService('N/A');
export default new EELicenseService();

View File

@ -29,6 +29,16 @@ import {
} from "../utils/errors";
import { validateUserClientForOrganization } from "../helpers/user";
import { validateServiceAccountClientForOrganization } from "../helpers/serviceAccount";
import {
EELicenseService
} from '../ee/services';
import {
getLicenseServerUrl
} from '../config';
import {
licenseServerKeyRequest,
licenseKeyRequest
} from '../config/request';
/**
* Validate accepted clients for organization with id [organizationId]
@ -228,30 +238,35 @@ const updateSubscriptionOrgQuantity = async ({
});
if (organization && organization.customerId) {
const quantity = await MembershipOrg.countDocuments({
organization: organizationId,
status: ACCEPTED,
});
const stripe = new Stripe(await getStripeSecretKey(), {
apiVersion: "2022-08-01",
});
const subscription = (
await stripe.subscriptions.list({
customer: organization.customerId,
})
).data[0];
stripeSubscription = await stripe.subscriptions.update(subscription.id, {
items: [
if (EELicenseService.instanceType === 'cloud') {
// instance of Infisical is a cloud instance
const quantity = await MembershipOrg.countDocuments({
organization: new Types.ObjectId(organizationId),
status: ACCEPTED,
});
await licenseServerKeyRequest.patch(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${organization.customerId}/cloud-plan`,
{
id: subscription.items.data[0].id,
price: subscription.items.data[0].price.id,
quantity,
},
],
});
quantity
}
);
}
if (EELicenseService.instanceType === 'enterprise-self-hosted') {
// instance of Infisical is an enterprise self-hosted instance
const usedSeats = await MembershipOrg.countDocuments({
status: ACCEPTED
});
await licenseKeyRequest.patch(
`${await getLicenseServerUrl()}/api/license/v1/license`,
{
usedSeats
}
);
}
}
return stripeSubscription;

View File

@ -89,7 +89,7 @@ const validateClientForWorkspace = async ({
requiredPermissions
});
return ({ membership });
return ({ membership, workspace });
}
if (authData.authMode === AUTH_MODE_SERVICE_ACCOUNT && authData.authPayload instanceof ServiceAccount) {
@ -123,7 +123,7 @@ const validateClientForWorkspace = async ({
requiredPermissions
});
return ({ membership });
return ({ membership, workspace });
}
throw UnauthorizedRequestError({

View File

@ -1,4 +1,3 @@
import mongoose from 'mongoose';
import dotenv from 'dotenv';
dotenv.config();
import express from 'express';
@ -6,6 +5,7 @@ import helmet from 'helmet';
import cors from 'cors';
import * as Sentry from '@sentry/node';
import { DatabaseService } from './services';
import { EELicenseService } from './ee/services';
import { setUpHealthEndpoint } from './services/health';
import { initSmtp } from './services/smtp';
import { TelemetryService } from './services';
@ -25,7 +25,9 @@ import {
workspace as eeWorkspaceRouter,
secret as eeSecretRouter,
secretSnapshot as eeSecretSnapshotRouter,
action as eeActionRouter
action as eeActionRouter,
organizations as eeOrganizationsRouter,
cloudProducts as eeCloudProductsRouter
} from './ee/routes/v1';
import {
signup as v1SignupRouter,
@ -74,14 +76,15 @@ import {
getNodeEnv,
getPort,
getSentryDSN,
getSiteURL,
getSmtpHost
getSiteURL
} from './config';
const main = async () => {
TelemetryService.logTelemetryMessage();
setTransporter(await initSmtp());
await EELicenseService.initGlobalFeatureSet();
await DatabaseService.initDatabase(await getMongoURL());
if ((await getNodeEnv()) !== 'test') {
Sentry.init({
@ -119,6 +122,8 @@ const main = async () => {
app.use('/api/v1/secret-snapshot', eeSecretSnapshotRouter);
app.use('/api/v1/workspace', eeWorkspaceRouter);
app.use('/api/v1/action', eeActionRouter);
app.use('/api/v1/organizations', eeOrganizationsRouter);
app.use('/api/v1/cloud-products', eeCloudProductsRouter);
// v1 routes (default)
app.use('/api/v1/signup', v1SignupRouter);

View File

@ -1,6 +1,6 @@
import { Octokit } from "@octokit/rest";
import { IIntegrationAuth } from "../models";
import request from "../config/request";
import { standardRequest } from "../config/request";
import {
INTEGRATION_AZURE_KEY_VAULT,
INTEGRATION_AWS_PARAMETER_STORE,
@ -134,7 +134,7 @@ const getApps = async ({
*/
const getAppsHeroku = async ({ accessToken }: { accessToken: string }) => {
const res = (
await request.get(`${INTEGRATION_HEROKU_API_URL}/apps`, {
await standardRequest.get(`${INTEGRATION_HEROKU_API_URL}/apps`, {
headers: {
Accept: "application/vnd.heroku+json; version=3",
Authorization: `Bearer ${accessToken}`,
@ -164,7 +164,7 @@ const getAppsVercel = async ({
accessToken: string;
}) => {
const res = (
await request.get(`${INTEGRATION_VERCEL_API_URL}/v9/projects`, {
await standardRequest.get(`${INTEGRATION_VERCEL_API_URL}/v9/projects`, {
headers: {
Authorization: `Bearer ${accessToken}`,
"Accept-Encoding": "application/json",
@ -208,7 +208,7 @@ const getAppsNetlify = async ({ accessToken }: { accessToken: string }) => {
filter: 'all'
});
const { data } = await request.get(
const { data } = await standardRequest.get(
`${INTEGRATION_NETLIFY_API_URL}/api/v1/sites`,
{
params,
@ -310,7 +310,7 @@ const getAppsGithub = async ({ accessToken }: { accessToken: string }) => {
*/
const getAppsRender = async ({ accessToken }: { accessToken: string }) => {
const res = (
await request.get(`${INTEGRATION_RENDER_API_URL}/v1/services`, {
await standardRequest.get(`${INTEGRATION_RENDER_API_URL}/v1/services`, {
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json",
@ -358,7 +358,7 @@ const getAppsRailway = async ({ accessToken }: { accessToken: string }) => {
projects: { edges },
},
},
} = await request.post(
} = await standardRequest.post(
INTEGRATION_RAILWAY_API_URL,
{
query,
@ -402,7 +402,7 @@ const getAppsFlyio = async ({ accessToken }: { accessToken: string }) => {
`;
const res = (
await request.post(
await standardRequest.post(
INTEGRATION_FLYIO_API_URL,
{
query,
@ -436,7 +436,7 @@ const getAppsFlyio = async ({ accessToken }: { accessToken: string }) => {
*/
const getAppsCircleCI = async ({ accessToken }: { accessToken: string }) => {
const res = (
await request.get(`${INTEGRATION_CIRCLECI_API_URL}/v1.1/projects`, {
await standardRequest.get(`${INTEGRATION_CIRCLECI_API_URL}/v1.1/projects`, {
headers: {
"Circle-Token": accessToken,
"Accept-Encoding": "application/json",
@ -455,7 +455,7 @@ const getAppsCircleCI = async ({ accessToken }: { accessToken: string }) => {
const getAppsTravisCI = async ({ accessToken }: { accessToken: string }) => {
const res = (
await request.get(`${INTEGRATION_TRAVISCI_API_URL}/repos`, {
await standardRequest.get(`${INTEGRATION_TRAVISCI_API_URL}/repos`, {
headers: {
Authorization: `token ${accessToken}`,
"Accept-Encoding": "application/json",
@ -502,7 +502,7 @@ const getAppsGitlab = async ({
per_page: String(perPage),
});
const { data } = await request.get(
const { data } = await standardRequest.get(
`${INTEGRATION_GITLAB_API_URL}/v4/groups/${teamId}/projects`,
{
params,
@ -530,7 +530,7 @@ const getAppsGitlab = async ({
// case: fetch projects for individual in GitLab
const { id } = (
await request.get(`${INTEGRATION_GITLAB_API_URL}/v4/user`, {
await standardRequest.get(`${INTEGRATION_GITLAB_API_URL}/v4/user`, {
headers: {
Authorization: `Bearer ${accessToken}`,
"Accept-Encoding": "application/json",
@ -544,7 +544,7 @@ const getAppsGitlab = async ({
per_page: String(perPage),
});
const { data } = await request.get(
const { data } = await standardRequest.get(
`${INTEGRATION_GITLAB_API_URL}/v4/users/${id}/projects`,
{
params,
@ -581,7 +581,7 @@ const getAppsGitlab = async ({
* @returns {String} apps.name - name of Supabase app
*/
const getAppsSupabase = async ({ accessToken }: { accessToken: string }) => {
const { data } = await request.get(
const { data } = await standardRequest.get(
`${INTEGRATION_SUPABASE_API_URL}/v1/projects`,
{
headers: {

View File

@ -1,4 +1,4 @@
import request from "../config/request";
import { standardRequest } from "../config/request";
import {
INTEGRATION_AZURE_KEY_VAULT,
INTEGRATION_HEROKU,
@ -142,7 +142,7 @@ const exchangeCodeAzure = async ({ code }: { code: string }) => {
const accessExpiresAt = new Date();
const res: ExchangeCodeAzureResponse = (
await request.post(
await standardRequest.post(
INTEGRATION_AZURE_TOKEN_URL,
new URLSearchParams({
grant_type: "authorization_code",
@ -178,7 +178,7 @@ const exchangeCodeHeroku = async ({ code }: { code: string }) => {
const accessExpiresAt = new Date();
const res: ExchangeCodeHerokuResponse = (
await request.post(
await standardRequest.post(
INTEGRATION_HEROKU_TOKEN_URL,
new URLSearchParams({
grant_type: "authorization_code",
@ -209,7 +209,7 @@ const exchangeCodeHeroku = async ({ code }: { code: string }) => {
*/
const exchangeCodeVercel = async ({ code }: { code: string }) => {
const res: ExchangeCodeVercelResponse = (
await request.post(
await standardRequest.post(
INTEGRATION_VERCEL_TOKEN_URL,
new URLSearchParams({
code: code,
@ -240,7 +240,7 @@ const exchangeCodeVercel = async ({ code }: { code: string }) => {
*/
const exchangeCodeNetlify = async ({ code }: { code: string }) => {
const res: ExchangeCodeNetlifyResponse = (
await request.post(
await standardRequest.post(
INTEGRATION_NETLIFY_TOKEN_URL,
new URLSearchParams({
grant_type: "authorization_code",
@ -252,14 +252,14 @@ const exchangeCodeNetlify = async ({ code }: { code: string }) => {
)
).data;
const res2 = await request.get("https://api.netlify.com/api/v1/sites", {
const res2 = await standardRequest.get("https://api.netlify.com/api/v1/sites", {
headers: {
Authorization: `Bearer ${res.access_token}`,
},
});
const res3 = (
await request.get("https://api.netlify.com/api/v1/accounts", {
await standardRequest.get("https://api.netlify.com/api/v1/accounts", {
headers: {
Authorization: `Bearer ${res.access_token}`,
},
@ -287,7 +287,7 @@ const exchangeCodeNetlify = async ({ code }: { code: string }) => {
*/
const exchangeCodeGithub = async ({ code }: { code: string }) => {
const res: ExchangeCodeGithubResponse = (
await request.get(INTEGRATION_GITHUB_TOKEN_URL, {
await standardRequest.get(INTEGRATION_GITHUB_TOKEN_URL, {
params: {
client_id: await getClientIdGitHub(),
client_secret: await getClientSecretGitHub(),
@ -321,7 +321,7 @@ const exchangeCodeGithub = async ({ code }: { code: string }) => {
const exchangeCodeGitlab = async ({ code }: { code: string }) => {
const accessExpiresAt = new Date();
const res: ExchangeCodeGitlabResponse = (
await request.post(
await standardRequest.post(
INTEGRATION_GITLAB_TOKEN_URL,
new URLSearchParams({
grant_type: "authorization_code",

View File

@ -1,4 +1,4 @@
import request from "../config/request";
import { standardRequest } from "../config/request";
import { IIntegrationAuth } from "../models";
import {
INTEGRATION_AZURE_KEY_VAULT,
@ -121,7 +121,7 @@ const exchangeRefreshAzure = async ({
refreshToken: string;
}) => {
const accessExpiresAt = new Date();
const { data }: { data: RefreshTokenAzureResponse } = await request.post(
const { data }: { data: RefreshTokenAzureResponse } = await standardRequest.post(
INTEGRATION_AZURE_TOKEN_URL,
new URLSearchParams({
client_id: await getClientIdAzure(),
@ -158,7 +158,7 @@ const exchangeRefreshHeroku = async ({
data,
}: {
data: RefreshTokenHerokuResponse;
} = await request.post(
} = await standardRequest.post(
INTEGRATION_HEROKU_TOKEN_URL,
new URLSearchParams({
grant_type: "refresh_token",
@ -193,7 +193,7 @@ const exchangeRefreshGitLab = async ({
data,
}: {
data: RefreshTokenGitLabResponse;
} = await request.post(
} = await standardRequest.post(
INTEGRATION_GITLAB_TOKEN_URL,
new URLSearchParams({
grant_type: "refresh_token",

View File

@ -37,8 +37,7 @@ import {
INTEGRATION_TRAVISCI_API_URL,
INTEGRATION_SUPABASE_API_URL
} from "../variables";
import request from '../config/request';
import axios from "axios";
import { standardRequest} from '../config/request';
/**
* Sync/push [secrets] to [app] in integration named [integration]
@ -215,7 +214,7 @@ const syncSecretsAzureKeyVault = async ({
let result: GetAzureKeyVaultSecret[] = [];
try {
while (url) {
const res = await request.get(url, {
const res = await standardRequest.get(url, {
headers: {
Authorization: `Bearer ${accessToken}`
}
@ -242,7 +241,7 @@ const syncSecretsAzureKeyVault = async ({
lastSlashIndex = getAzureKeyVaultSecret.id.lastIndexOf('/');
}
const azureKeyVaultSecret = await request.get(`${getAzureKeyVaultSecret.id}?api-version=7.3`, {
const azureKeyVaultSecret = await standardRequest.get(`${getAzureKeyVaultSecret.id}?api-version=7.3`, {
headers: {
'Authorization': `Bearer ${accessToken}`
}
@ -308,7 +307,7 @@ const syncSecretsAzureKeyVault = async ({
while (!isSecretSet && maxTries > 0) {
// try to set secret
try {
await request.put(
await standardRequest.put(
`${integration.app}/secrets/${key}?api-version=7.3`,
{
value
@ -325,7 +324,7 @@ const syncSecretsAzureKeyVault = async ({
} catch (err) {
const error: any = err;
if (error?.response?.data?.error?.innererror?.code === 'ObjectIsDeletedButRecoverable') {
await request.post(
await standardRequest.post(
`${integration.app}/deletedsecrets/${key}/recover?api-version=7.3`, {},
{
headers: {
@ -355,7 +354,7 @@ const syncSecretsAzureKeyVault = async ({
for await (const deleteSecret of deleteSecrets) {
const { key } = deleteSecret;
await request.delete(`${integration.app}/secrets/${key}?api-version=7.3`, {
await standardRequest.delete(`${integration.app}/secrets/${key}?api-version=7.3`, {
headers: {
'Authorization': `Bearer ${accessToken}`
}
@ -568,7 +567,7 @@ const syncSecretsHeroku = async ({
}) => {
try {
const herokuSecrets = (
await request.get(
await standardRequest.get(
`${INTEGRATION_HEROKU_API_URL}/apps/${integration.app}/config-vars`,
{
headers: {
@ -586,7 +585,7 @@ const syncSecretsHeroku = async ({
}
});
await request.patch(
await standardRequest.patch(
`${INTEGRATION_HEROKU_API_URL}/apps/${integration.app}/config-vars`,
secrets,
{
@ -642,7 +641,7 @@ const syncSecretsVercel = async ({
: {}),
};
const vercelSecrets: VercelSecret[] = (await request.get(
const vercelSecrets: VercelSecret[] = (await standardRequest.get(
`${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env`,
{
params,
@ -675,7 +674,7 @@ const syncSecretsVercel = async ({
for await (const vercelSecret of vercelSecrets) {
if (vercelSecret.type === 'encrypted') {
// case: secret is encrypted -> need to decrypt
const decryptedSecret = (await request.get(
const decryptedSecret = (await standardRequest.get(
`${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${vercelSecret.id}`,
{
params,
@ -747,7 +746,7 @@ const syncSecretsVercel = async ({
// Sync/push new secrets
if (newSecrets.length > 0) {
await request.post(
await standardRequest.post(
`${INTEGRATION_VERCEL_API_URL}/v10/projects/${integration.app}/env`,
newSecrets,
{
@ -763,7 +762,7 @@ const syncSecretsVercel = async ({
for await (const secret of updateSecrets) {
if (secret.type !== 'sensitive') {
const { id, ...updatedSecret } = secret;
await request.patch(
await standardRequest.patch(
`${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`,
updatedSecret,
{
@ -778,7 +777,7 @@ const syncSecretsVercel = async ({
}
for await (const secret of deleteSecrets) {
await request.delete(
await standardRequest.delete(
`${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`,
{
params,
@ -837,7 +836,7 @@ const syncSecretsNetlify = async ({
});
const res = (
await request.get(
await standardRequest.get(
`${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env`,
{
params: getParams,
@ -951,7 +950,7 @@ const syncSecretsNetlify = async ({
});
if (newSecrets.length > 0) {
await request.post(
await standardRequest.post(
`${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env`,
newSecrets,
{
@ -966,7 +965,7 @@ const syncSecretsNetlify = async ({
if (updateSecrets.length > 0) {
updateSecrets.forEach(async (secret: NetlifySecret) => {
await request.patch(
await standardRequest.patch(
`${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}`,
{
context: secret.values[0].context,
@ -985,7 +984,7 @@ const syncSecretsNetlify = async ({
if (deleteSecrets.length > 0) {
deleteSecrets.forEach(async (key: string) => {
await request.delete(
await standardRequest.delete(
`${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${key}`,
{
params: syncParams,
@ -1000,7 +999,7 @@ const syncSecretsNetlify = async ({
if (deleteSecretValues.length > 0) {
deleteSecretValues.forEach(async (secret: NetlifySecret) => {
await request.delete(
await standardRequest.delete(
`${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}/value/${secret.values[0].id}`,
{
params: syncParams,
@ -1151,7 +1150,7 @@ const syncSecretsRender = async ({
accessToken: string;
}) => {
try {
await request.put(
await standardRequest.put(
`${INTEGRATION_RENDER_API_URL}/v1/services/${integration.appId}/env-vars`,
Object.keys(secrets).map((key) => ({
key,
@ -1203,7 +1202,7 @@ const syncSecretsRailway = async ({
variables: secrets
};
await request.post(INTEGRATION_RAILWAY_API_URL, {
await standardRequest.post(INTEGRATION_RAILWAY_API_URL, {
query,
variables: {
input,
@ -1261,7 +1260,7 @@ const syncSecretsFlyio = async ({
}
`;
await request.post(INTEGRATION_FLYIO_API_URL, {
await standardRequest.post(INTEGRATION_FLYIO_API_URL, {
query: SetSecrets,
variables: {
input: {
@ -1296,7 +1295,7 @@ const syncSecretsFlyio = async ({
}
}`;
const getSecretsRes = (await request.post(INTEGRATION_FLYIO_API_URL, {
const getSecretsRes = (await standardRequest.post(INTEGRATION_FLYIO_API_URL, {
query: GetSecrets,
variables: {
appName: integration.app,
@ -1332,7 +1331,7 @@ const syncSecretsFlyio = async ({
}
}`;
await request.post(INTEGRATION_FLYIO_API_URL, {
await standardRequest.post(INTEGRATION_FLYIO_API_URL, {
query: DeleteSecrets,
variables: {
input: {
@ -1373,7 +1372,7 @@ const syncSecretsCircleCI = async ({
}) => {
try {
const circleciOrganizationDetail = (
await request.get(`${INTEGRATION_CIRCLECI_API_URL}/v2/me/collaborations`, {
await standardRequest.get(`${INTEGRATION_CIRCLECI_API_URL}/v2/me/collaborations`, {
headers: {
"Circle-Token": accessToken,
"Accept-Encoding": "application/json",
@ -1386,7 +1385,7 @@ const syncSecretsCircleCI = async ({
// sync secrets to CircleCI
Object.keys(secrets).forEach(
async (key) =>
await request.post(
await standardRequest.post(
`${INTEGRATION_CIRCLECI_API_URL}/v2/project/${slug}/${integration.app}/envvar`,
{
name: key,
@ -1403,7 +1402,7 @@ const syncSecretsCircleCI = async ({
// get secrets from CircleCI
const getSecretsRes = (
await request.get(
await standardRequest.get(
`${INTEGRATION_CIRCLECI_API_URL}/v2/project/${slug}/${integration.app}/envvar`,
{
headers: {
@ -1417,7 +1416,7 @@ const syncSecretsCircleCI = async ({
// delete secrets from CircleCI
getSecretsRes.forEach(async (sec: any) => {
if (!(sec.name in secrets)) {
await request.delete(
await standardRequest.delete(
`${INTEGRATION_CIRCLECI_API_URL}/v2/project/${slug}/${integration.app}/envvar/${sec.name}`,
{
headers: {
@ -1454,7 +1453,7 @@ const syncSecretsTravisCI = async ({
try {
// get secrets from travis-ci
const getSecretsRes = (
await request.get(
await standardRequest.get(
`${INTEGRATION_TRAVISCI_API_URL}/settings/env_vars?repository_id=${integration.appId}`,
{
headers: {
@ -1476,7 +1475,7 @@ const syncSecretsTravisCI = async ({
if (!(key in getSecretsRes)) {
// case: secret does not exist in travis ci
// -> add secret
await request.post(
await standardRequest.post(
`${INTEGRATION_TRAVISCI_API_URL}/settings/env_vars?repository_id=${integration.appId}`,
{
env_var: {
@ -1495,7 +1494,7 @@ const syncSecretsTravisCI = async ({
} else {
// case: secret exists in travis ci
// -> update/set secret
await request.patch(
await standardRequest.patch(
`${INTEGRATION_TRAVISCI_API_URL}/settings/env_vars/${getSecretsRes[key].id}?repository_id=${getSecretsRes[key].repository_id}`,
{
env_var: {
@ -1517,7 +1516,7 @@ const syncSecretsTravisCI = async ({
for await (const key of Object.keys(getSecretsRes)) {
if (!(key in secrets)){
// delete secret
await request.delete(
await standardRequest.delete(
`${INTEGRATION_TRAVISCI_API_URL}/settings/env_vars/${getSecretsRes[key].id}?repository_id=${getSecretsRes[key].repository_id}`,
{
headers: {
@ -1554,9 +1553,15 @@ const syncSecretsGitLab = async ({
accessToken: string;
}) => {
try {
interface GitLabSecret {
key: string;
value: string;
environment_scope: string;
}
// get secrets from gitlab
const getSecretsRes = (
await request.get(
const getSecretsRes: GitLabSecret[] = (
await standardRequest.get(
`${INTEGRATION_GITLAB_API_URL}/v4/projects/${integration?.appId}/variables`,
{
headers: {
@ -1565,12 +1570,16 @@ const syncSecretsGitLab = async ({
},
}
)
).data;
)
.data
.filter((secret: GitLabSecret) =>
secret.environment_scope === integration.targetEnvironment
);
for await (const key of Object.keys(secrets)) {
const existingSecret = getSecretsRes.find((s: any) => s.key == key);
if (!existingSecret) {
await request.post(
await standardRequest.post(
`${INTEGRATION_GITLAB_API_URL}/v4/projects/${integration?.appId}/variables`,
{
key: key,
@ -1578,7 +1587,7 @@ const syncSecretsGitLab = async ({
protected: false,
masked: false,
raw: false,
environment_scope:'*'
environment_scope: integration.targetEnvironment
},
{
headers: {
@ -1589,29 +1598,31 @@ const syncSecretsGitLab = async ({
}
)
} else {
// udpate secret
await request.put(
`${INTEGRATION_GITLAB_API_URL}/v4/projects/${integration?.appId}/variables/${existingSecret.key}`,
{
...existingSecret,
value: secrets[existingSecret.key]
},
{
headers: {
"Authorization": `Bearer ${accessToken}`,
"Content-Type": "application/json",
"Accept-Encoding": "application/json",
// update secret
if (secrets[key] !== existingSecret.value) {
await standardRequest.put(
`${INTEGRATION_GITLAB_API_URL}/v4/projects/${integration?.appId}/variables/${existingSecret.key}?filter[environment_scope]=${integration.targetEnvironment}`,
{
...existingSecret,
value: secrets[existingSecret.key]
},
}
)
{
headers: {
"Authorization": `Bearer ${accessToken}`,
"Content-Type": "application/json",
"Accept-Encoding": "application/json",
},
}
);
}
}
}
// delete secrets
for await (const sec of getSecretsRes) {
if (!(sec.key in secrets)) {
await request.delete(
`${INTEGRATION_GITLAB_API_URL}/v4/projects/${integration?.appId}/variables/${sec.key}`,
await standardRequest.delete(
`${INTEGRATION_GITLAB_API_URL}/v4/projects/${integration?.appId}/variables/${sec.key}?filter[environment_scope]=${integration.targetEnvironment}`,
{
headers: {
"Authorization": `Bearer ${accessToken}`,
@ -1620,7 +1631,7 @@ const syncSecretsGitLab = async ({
);
}
}
}catch (err) {
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error("Failed to sync secrets to GitLab");
@ -1645,7 +1656,7 @@ const syncSecretsSupabase = async ({
accessToken: string;
}) => {
try {
const { data: getSecretsRes } = await request.get(
const { data: getSecretsRes } = await standardRequest.get(
`${INTEGRATION_SUPABASE_API_URL}/v1/projects/${integration.appId}/secrets`,
{
headers: {
@ -1665,7 +1676,7 @@ const syncSecretsSupabase = async ({
}
);
await request.post(
await standardRequest.post(
`${INTEGRATION_SUPABASE_API_URL}/v1/projects/${integration.appId}/secrets`,
modifiedFormatForSecretInjection,
{
@ -1683,7 +1694,7 @@ const syncSecretsSupabase = async ({
}
});
await request.delete(
await standardRequest.delete(
`${INTEGRATION_SUPABASE_API_URL}/v1/projects/${integration.appId}/secrets`,
{
headers: {

View File

@ -5,7 +5,7 @@ import {
INTEGRATION_GITLAB,
INTEGRATION_GITLAB_API_URL
} from '../variables';
import request from '../config/request';
import { standardRequest } from '../config/request';
interface Team {
name: string;
@ -56,7 +56,7 @@ const getTeamsGitLab = async ({
accessToken: string;
}) => {
let teams: Team[] = [];
const res = (await request.get(
const res = (await standardRequest.get(
`${INTEGRATION_GITLAB_API_URL}/v4/groups`,
{
headers: {

View File

@ -1,8 +1,6 @@
import { Request, Response, NextFunction } from 'express';
import { Types } from 'mongoose';
import { validateMembership } from '../helpers/membership';
import { validateClientForWorkspace } from '../helpers/workspace';
import { UnauthorizedRequestError } from '../utils/errors';
type req = 'params' | 'body' | 'query';
@ -31,7 +29,7 @@ const requireWorkspaceAuth = ({
const environment = locationEnvironment ? req[locationEnvironment]?.environment : undefined;
// validate clients
const { membership } = await validateClientForWorkspace({
const { membership, workspace } = await validateClientForWorkspace({
authData: req.authData,
workspaceId: new Types.ObjectId(workspaceId),
environment,
@ -43,6 +41,10 @@ const requireWorkspaceAuth = ({
if (membership) {
req.membership = membership;
}
if (workspace) {
req.workspace = workspace;
}
return next();
};

View File

@ -21,6 +21,7 @@ export interface IIntegration {
workspace: Types.ObjectId;
environment: string;
isActive: boolean;
url: string;
app: string;
appId: string;
owner: string;
@ -63,6 +64,11 @@ const integrationSchema = new Schema<IIntegration>(
type: Boolean,
required: true,
},
url: {
// for custom self-hosted integrations (e.g. self-hosted GitHub enterprise)
type: String,
default: null
},
app: {
// name of app in provider
type: String,

View File

@ -28,6 +28,7 @@ require (
github.com/chzyer/readline v1.5.1 // indirect
github.com/danieljoos/wincred v1.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/denisbrodbeck/machineid v1.0.1 // indirect
github.com/dvsekhvalnov/jose2go v1.5.0 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/go-openapi/errors v0.20.2 // indirect
@ -47,11 +48,13 @@ require (
github.com/oklog/ulid v1.3.1 // indirect
github.com/pelletier/go-toml v1.9.3 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/posthog/posthog-go v0.0.0-20221221115252-24dfed35d71a // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/spf13/afero v1.6.0 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
go.mongodb.org/mongo-driver v1.10.0 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect

View File

@ -69,6 +69,7 @@ github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnht
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
@ -76,6 +77,8 @@ github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnG
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
github.com/dvsekhvalnov/jose2go v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM=
github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@ -286,6 +289,8 @@ github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZ
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/posthog/posthog-go v0.0.0-20221221115252-24dfed35d71a h1:Ey0XWvrg6u6hyIn1Kd/jCCmL+bMv9El81tvuGBbxZGg=
github.com/posthog/posthog-go v0.0.0-20221221115252-24dfed35d71a/go.mod h1:oa2sAs9tGai3VldabTV0eWejt/O4/OOD7azP8GaikqU=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
@ -295,9 +300,11 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc=
github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
@ -330,9 +337,12 @@ github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c h1:3lbZUMbMiGUW/LMkfsEABsc5zNT9+b1CvsJx47JzJ8g=
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=

View File

@ -2,6 +2,7 @@ package api
import (
"fmt"
"net/http"
"github.com/Infisical/infisical-merge/packages/config"
"github.com/go-resty/resty/v2"
@ -179,6 +180,19 @@ func CallLogin2V2(httpClient *resty.Client, request GetLoginTwoV2Request) (GetLo
SetBody(request).
Post(fmt.Sprintf("%v/v2/auth/login2", config.INFISICAL_URL))
cookies := response.Cookies()
// Find a cookie by name
cookieName := "jid"
var refreshToken *http.Cookie
for _, cookie := range cookies {
if cookie.Name == cookieName {
refreshToken = cookie
break
}
}
loginTwoV2Response.RefreshToken = refreshToken.Value
if err != nil {
return GetLoginTwoV2Response{}, fmt.Errorf("CallLogin2V2: Unable to complete api request [err=%s]", err)
}
@ -247,3 +261,26 @@ func CallGetAccessibleEnvironments(httpClient *resty.Client, request GetAccessib
return accessibleEnvironmentsResponse, nil
}
func CallGetNewAccessTokenWithRefreshToken(httpClient *resty.Client, refreshToken string) (GetNewAccessTokenWithRefreshTokenResponse, error) {
var newAccessToken GetNewAccessTokenWithRefreshTokenResponse
response, err := httpClient.
R().
SetResult(&newAccessToken).
SetHeader("User-Agent", USER_AGENT).
SetCookie(&http.Cookie{
Name: "jid",
Value: refreshToken,
}).
Post(fmt.Sprintf("%v/v1/auth/token", config.INFISICAL_URL))
if err != nil {
return GetNewAccessTokenWithRefreshTokenResponse{}, err
}
if response.IsError() {
return GetNewAccessTokenWithRefreshTokenResponse{}, fmt.Errorf("CallGetNewAccessTokenWithRefreshToken: Unsuccessful response: [response=%v]", response)
}
return newAccessToken, nil
}

View File

@ -281,6 +281,7 @@ type GetLoginTwoV2Response struct {
ProtectedKey string `json:"protectedKey"`
ProtectedKeyIV string `json:"protectedKeyIV"`
ProtectedKeyTag string `json:"protectedKeyTag"`
RefreshToken string `json:"RefreshToken"`
}
type VerifyMfaTokenRequest struct {
@ -314,3 +315,7 @@ type VerifyMfaTokenErrorResponse struct {
Application string `json:"application"`
Extra []interface{} `json:"extra"`
}
type GetNewAccessTokenWithRefreshTokenResponse struct {
Token string `json:"token"`
}

View File

@ -11,6 +11,7 @@ import (
"github.com/Infisical/infisical-merge/packages/models"
"github.com/Infisical/infisical-merge/packages/util"
"github.com/posthog/posthog-go"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)
@ -95,6 +96,8 @@ var exportCmd = &cobra.Command{
}
fmt.Print(output)
Telemetry.CaptureEvent("cli-command:export", posthog.NewProperties().Set("secretsCount", len(secrets)).Set("version", util.CLI_VERSION))
},
}

View File

@ -12,6 +12,7 @@ import (
"github.com/Infisical/infisical-merge/packages/util"
"github.com/go-resty/resty/v2"
"github.com/manifoldco/promptui"
"github.com/posthog/posthog-go"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)
@ -78,6 +79,9 @@ var initCmd = &cobra.Command{
if err != nil {
util.HandleError(err)
}
Telemetry.CaptureEvent("cli-command:init", posthog.NewProperties().Set("version", util.CLI_VERSION))
},
}

View File

@ -22,6 +22,7 @@ import (
"github.com/fatih/color"
"github.com/go-resty/resty/v2"
"github.com/manifoldco/promptui"
"github.com/posthog/posthog-go"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"golang.org/x/crypto/argon2"
@ -96,7 +97,7 @@ var loginCmd = &cobra.Command{
loginOneResponse, loginTwoResponse, err := getFreshUserCredentials(email, password)
if err != nil {
fmt.Println("Unable to authenticate with the provided credentials, please try again")
log.Warn().Msg("Unable to authenticate with the provided credentials, please ensure your email and password are correct")
log.Debug().Err(err)
return
}
@ -243,9 +244,10 @@ var loginCmd = &cobra.Command{
}
userCredentialsToBeStored := &models.UserCredentials{
Email: email,
PrivateKey: string(decryptedPrivateKey),
JTWToken: loginTwoResponse.Token,
Email: email,
PrivateKey: string(decryptedPrivateKey),
JTWToken: loginTwoResponse.Token,
RefreshToken: loginTwoResponse.RefreshToken,
}
err = util.StoreUserCredsInKeyRing(userCredentialsToBeStored)
@ -275,6 +277,7 @@ var loginCmd = &cobra.Command{
plainBold.Println("\nQuick links")
fmt.Println("- Learn to inject secrets into your application at https://infisical.com/docs/cli/usage")
fmt.Println("- Stuck? Join our slack for quick support https://infisical.com/slack")
Telemetry.CaptureEvent("cli-command:login", posthog.NewProperties().Set("infisical-backend", config.INFISICAL_URL).Set("version", util.CLI_VERSION))
},
}
@ -412,7 +415,7 @@ func getFreshUserCredentials(email string, password string) (*api.GetLoginOneV2R
})
if err != nil {
util.HandleError(err)
return nil, nil, err
}
// **** Login 2

View File

@ -0,0 +1,20 @@
# MANAGED BY INFISICAL CLI (Do not modify): START
infisicalScanEnabled=$(git config --bool hooks.infisical-scan)
if [ "$infisicalScanEnabled" != "false" ]; then
infisical scan git-changes -v --staged
exitCode=$?
if [ $exitCode -eq 1 ]; then
echo "Commit blocked: Infisical scan has uncovered secrets in your git commit"
echo "To disable the Infisical scan precommit hook run the following command:"
echo ""
echo " git config hooks.infisical-scan false"
echo ""
exit 1
fi
else
echo 'Warning: infisical scan precommit disabled'
fi
# MANAGED BY INFISICAL CLI (Do not modify): END

View File

@ -0,0 +1,20 @@
#!/bin/sh
# MANAGED BY INFISICAL CLI (Do not modify): START
infisicalScanEnabled=$(git config --bool hooks.infisical-scan)
if [ "$infisicalScanEnabled" != "false" ]; then
infisical scan git-changes -v --staged
exitCode=$?
if [ $exitCode -eq 1 ]; then
echo "Commit blocked: Infisical scan has uncovered secrets in your git commit"
echo "To disable the Infisical scan precommit hook run the following command:"
echo ""
echo " git config hooks.infisical-scan false"
echo ""
exit 1
fi
else
echo 'Warning: infisical scan precommit disabled'
fi
# MANAGED BY INFISICAL CLI (Do not modify): END

View File

@ -7,6 +7,7 @@ import (
"os"
"github.com/Infisical/infisical-merge/packages/util"
"github.com/posthog/posthog-go"
"github.com/spf13/cobra"
)
@ -37,6 +38,7 @@ var resetCmd = &cobra.Command{
util.DeleteBackupSecrets()
util.PrintSuccessMessage("Reset successful")
Telemetry.CaptureEvent("cli-command:reset", posthog.NewProperties().Set("version", util.CLI_VERSION))
},
}

View File

@ -12,9 +12,12 @@ import (
"github.com/spf13/cobra"
"github.com/Infisical/infisical-merge/packages/config"
"github.com/Infisical/infisical-merge/packages/telemetry"
"github.com/Infisical/infisical-merge/packages/util"
)
var Telemetry *telemetry.Telemetry
var rootCmd = &cobra.Command{
Use: "infisical",
Short: "Infisical CLI is used to inject environment variables into any process",
@ -35,6 +38,7 @@ func Execute() {
func init() {
cobra.OnInitialize(initLog)
rootCmd.PersistentFlags().StringP("log-level", "l", "info", "log level (trace, debug, info, warn, error, fatal)")
rootCmd.PersistentFlags().Bool("telemetry", true, "Infisical collects non-sensitive telemetry data to enhance features and improve user experience. Participation is voluntary")
rootCmd.PersistentFlags().StringVar(&config.INFISICAL_URL, "domain", util.INFISICAL_DEFAULT_API_URL, "Point the CLI to your own backend [can also set via environment variable name: INFISICAL_API_URL]")
rootCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
if !util.IsRunningInDocker() {
@ -49,6 +53,9 @@ func init() {
config.INFISICAL_URL = envInfisicalBackendUrl
}
}
isTelemetryOn, _ := rootCmd.PersistentFlags().GetBool("telemetry")
Telemetry = telemetry.NewTelemetry(isTelemetryOn)
}
func initLog() {

View File

@ -15,6 +15,7 @@ import (
"github.com/Infisical/infisical-merge/packages/models"
"github.com/Infisical/infisical-merge/packages/util"
"github.com/fatih/color"
"github.com/posthog/posthog-go"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)
@ -125,6 +126,8 @@ var runCmd = &cobra.Command{
log.Debug().Msgf("injecting the following environment variables into shell: %v", env)
Telemetry.CaptureEvent("cli-command:run", posthog.NewProperties().Set("secretsCount", len(secrets)).Set("environment", environmentName).Set("isUsingServiceToken", infisicalToken != "").Set("single-command", strings.Join(args, " ")).Set("multi-command", cmd.Flag("command").Value.String()).Set("version", util.CLI_VERSION))
if cmd.Flags().Changed("command") {
command := cmd.Flag("command").Value.String()

View File

@ -23,14 +23,21 @@
package cmd
import (
_ "embed"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"github.com/Infisical/infisical-merge/config"
"github.com/Infisical/infisical-merge/detect"
"github.com/Infisical/infisical-merge/packages/util"
"github.com/Infisical/infisical-merge/report"
"github.com/manifoldco/promptui"
"github.com/posthog/posthog-go"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/spf13/viper"
@ -43,6 +50,17 @@ order of precedence:
3. (--source/-s)/.infisical-scan.toml
If none of the three options are used, then Infisical will use the default scan config`
//go:embed pre-commit-script/pre-commit.sh
var preCommitTemplate []byte
//go:embed pre-commit-script/pre-commit-without-bang.sh
var preCommitTemplateAppend []byte
const (
defaultHooksPath = ".git/hooks/"
preCommitFile = "pre-commit"
)
func init() {
// scan flag for only scan command
scanCmd.Flags().String("log-opts", "", "git log options")
@ -75,6 +93,9 @@ func init() {
// add flags to main
scanCmd.AddCommand(scanGitChangesCmd)
rootCmd.AddCommand(scanCmd)
installCmd.Flags().Bool("pre-commit-hook", false, "installs pre commit hook for Git repository")
scanCmd.AddCommand(installCmd)
}
func initScanConfig(cmd *cobra.Command) {
@ -130,6 +151,50 @@ func initScanConfig(cmd *cobra.Command) {
}
}
var installCmd = &cobra.Command{
Use: "install",
Short: "Install scanning scripts and tools. Use --help flag to see all options",
Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, args []string) {
installPrecommit := cmd.Flags().Changed("pre-commit-hook")
if installPrecommit {
hooksPath, err := getHooksPath()
if err != nil {
fmt.Printf("Error: %s\n", err)
return
}
if hooksPath != ".git/hooks" {
defaultHookOverride, err := overrideDefaultHooksPath(hooksPath)
if err != nil {
fmt.Printf("Error: %s\n", err)
}
if defaultHookOverride {
ConfigureGitHooksPath()
log.Info().Msgf("To switch back previous githooks manager run: git config core.hooksPath %s\n", hooksPath)
return
} else {
log.Warn().Msgf("To automatically configure this hook, you need to switch the path of the Hooks. Alternatively, you can manually configure this hook by setting your pre-commit script to run command [infisical scan git-changes -v --staged].\n")
return
}
}
err = createOrUpdatePreCommitFile(hooksPath)
if err != nil {
fmt.Printf("Error: %s\n", err)
return
}
log.Info().Msgf("Pre-commit hook successfully added. Infisical scan should now run on each commit you make\n")
Telemetry.CaptureEvent("cli-command:install --pre-commit-hook", posthog.NewProperties().Set("version", util.CLI_VERSION))
return
}
}}
var scanCmd = &cobra.Command{
Use: "scan",
Short: "Scan for leaked secrets in git history, directories, and files",
@ -270,6 +335,8 @@ var scanCmd = &cobra.Command{
}
}
Telemetry.CaptureEvent("cli-command:scan", posthog.NewProperties().Set("risks", len(findings)).Set("version", util.CLI_VERSION))
// write report if desired
reportPath, _ := cmd.Flags().GetString("report-path")
ext, _ := cmd.Flags().GetString("report-format")
@ -375,6 +442,8 @@ var scanGitChangesCmd = &cobra.Command{
log.Info().Msg("no leaks found")
}
Telemetry.CaptureEvent("cli-command:scan git-changes", posthog.NewProperties().Set("risks", len(findings)).Set("version", util.CLI_VERSION))
reportPath, _ := cmd.Flags().GetString("report-path")
ext, _ := cmd.Flags().GetString("report-format")
if reportPath != "" {
@ -411,3 +480,107 @@ func FormatDuration(d time.Duration) string {
}
return d.Round(scale / 100).String()
}
func overrideDefaultHooksPath(managedHook string) (bool, error) {
YES := "Yes"
NO := "No"
options := []string{YES, NO}
optionsPrompt := promptui.Select{
Label: fmt.Sprintf("Your hooks path is set to [%s] but needs to be [.git/hooks] for automatic configuration. Would you like to switch? ", managedHook),
Items: options,
Size: 2,
}
_, selectedOption, err := optionsPrompt.Run()
if err != nil {
return false, err
}
return selectedOption == YES, err
}
func ConfigureGitHooksPath() {
cmd := exec.Command("git", "config", "core.hooksPath", ".git/hooks")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Fatal().Msgf("Failed to configure git hooks path: %v", err)
}
}
// GetGitRoot returns the root directory of the current Git repository.
func GetGitRoot() (string, error) {
cmd := exec.Command("git", "rev-parse", "--show-toplevel")
output, err := cmd.Output()
if err != nil {
return "", fmt.Errorf("failed to get git root directory: %w", err)
}
gitRoot := strings.TrimSpace(string(output)) // Remove any trailing newline
return gitRoot, nil
}
func getHooksPath() (string, error) {
out, err := exec.Command("git", "config", "core.hooksPath").Output()
if err != nil {
if len(out) == 0 {
out = []byte(".git/hooks") // set the default hook
} else {
log.Error().Msgf("Failed to get Git hooks path: %s\nOutput: %s\n", err, out)
}
}
hooksPath := strings.TrimSpace(string(out))
return hooksPath, nil
}
func createOrUpdatePreCommitFile(hooksPath string) error {
// File doesn't exist, create a new one
rootGitRepoPath, err := GetGitRoot()
if err != nil {
return err
}
filePath := fmt.Sprintf("%s/%s/%s", rootGitRepoPath, hooksPath, preCommitFile)
_, err = os.Stat(filePath)
if err == nil {
// File already exists, check if it contains the managed comments
content, err := ioutil.ReadFile(filePath)
if err != nil {
return fmt.Errorf("failed to read pre-commit file: %s", err)
}
if strings.Contains(string(content), "# MANAGED BY INFISICAL CLI (Do not modify): START") &&
strings.Contains(string(content), "# MANAGED BY INFISICAL CLI (Do not modify): END") {
return nil
}
// File already exists, append the template content
file, err := os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY, 0755)
if err != nil {
return fmt.Errorf("failed to open pre-commit file: %s", err)
}
defer file.Close()
_, err = file.Write(preCommitTemplateAppend)
if err != nil {
return fmt.Errorf("failed to append to pre-commit file: %s", err)
}
} else if os.IsNotExist(err) {
err = os.WriteFile(filePath, preCommitTemplate, 0755)
if err != nil {
return fmt.Errorf("failed to create pre-commit file: %s", err)
}
} else {
// Error occurred while checking file status
return fmt.Errorf("failed to check pre-commit file status: %s", err)
}
return nil
}

View File

@ -19,6 +19,7 @@ import (
"github.com/Infisical/infisical-merge/packages/util"
"github.com/Infisical/infisical-merge/packages/visualize"
"github.com/go-resty/resty/v2"
"github.com/posthog/posthog-go"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)
@ -63,6 +64,7 @@ var secretsCmd = &cobra.Command{
}
visualize.PrintAllSecretDetails(secrets)
Telemetry.CaptureEvent("cli-command:secrets", posthog.NewProperties().Set("secretCount", len(secrets)).Set("version", util.CLI_VERSION))
},
}
@ -266,6 +268,8 @@ var secretsSetCmd = &cobra.Command{
}
visualize.Table(headers, rows)
Telemetry.CaptureEvent("cli-command:secrets set", posthog.NewProperties().Set("version", util.CLI_VERSION))
},
}
@ -333,6 +337,7 @@ var secretsDeleteCmd = &cobra.Command{
fmt.Printf("secret name(s) [%v] have been deleted from your project \n", strings.Join(args, ", "))
Telemetry.CaptureEvent("cli-command:secrets delete", posthog.NewProperties().Set("secretCount", len(secrets)).Set("version", util.CLI_VERSION))
},
}
@ -377,6 +382,7 @@ func getSecretsByNames(cmd *cobra.Command, args []string) {
}
visualize.PrintAllSecretDetails(requestedSecrets)
Telemetry.CaptureEvent("cli-command:secrets get", posthog.NewProperties().Set("secretCount", len(secrets)).Set("version", util.CLI_VERSION))
}
func generateExampleEnv(cmd *cobra.Command, args []string) {
@ -565,6 +571,8 @@ func generateExampleEnv(cmd *cobra.Command, args []string) {
fmt.Println(strings.Join(dashedList, ""))
}
fmt.Println(strings.Join(fullyGeneratedDocuments, ""))
Telemetry.CaptureEvent("cli-command:generate-example-env", posthog.NewProperties().Set("secretCount", len(secrets)).Set("version", util.CLI_VERSION))
}
func CenterString(s string, numStars int) string {

View File

@ -8,6 +8,7 @@ import (
"github.com/Infisical/infisical-merge/packages/models"
"github.com/Infisical/infisical-merge/packages/util"
"github.com/manifoldco/promptui"
"github.com/posthog/posthog-go"
"github.com/spf13/cobra"
)
@ -78,6 +79,8 @@ var switchCmd = &cobra.Command{
if err != nil {
util.HandleError(err, "")
}
Telemetry.CaptureEvent("cli-command:user switch", posthog.NewProperties().Set("numberOfLoggedInProfiles", len(loggedInProfiles)).Set("version", util.CLI_VERSION))
},
}
@ -174,7 +177,7 @@ var domainCmd = &cobra.Command{
if err != nil {
util.HandleError(err, "")
}
Telemetry.CaptureEvent("cli-command:user domain", posthog.NewProperties().Set("version", util.CLI_VERSION))
},
}

View File

@ -8,6 +8,7 @@ import (
"github.com/99designs/keyring"
"github.com/Infisical/infisical-merge/packages/util"
"github.com/posthog/posthog-go"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)
@ -48,6 +49,8 @@ var vaultSetCmd = &cobra.Command{
}
fmt.Printf("\nSuccessfully, switched vault backend from [%s] to [%s]. Please login in again to store your login details in the new vault with [infisical login]", currentVaultBackend, wantedVaultTypeName)
Telemetry.CaptureEvent("cli-command:vault set", posthog.NewProperties().Set("currentVault", currentVaultBackend).Set("wantedVault", wantedVaultTypeName).Set("version", util.CLI_VERSION))
} else {
log.Error().Msgf("The requested vault type [%s] is not available on this system. Only the following vault backends are available for you system: %s", wantedVaultTypeName, keyring.AvailableBackends())
}
@ -76,6 +79,8 @@ func printAvailableVaultBackends() {
log.Error().Msgf("printAvailableVaultBackends: unable to print the available vault backend because of error [err=%s]", err)
}
Telemetry.CaptureEvent("cli-command:vault", posthog.NewProperties().Set("currentVault", currentVaultBackend).Set("version", util.CLI_VERSION))
fmt.Printf("\n\nYou are currently using [%s] vault to store your login credentials", string(currentVaultBackend))
}

View File

@ -5,9 +5,10 @@ import (
)
type UserCredentials struct {
Email string `json:"email"`
PrivateKey string `json:"privateKey"`
JTWToken string `json:"JTWToken"`
Email string `json:"email"`
PrivateKey string `json:"privateKey"`
JTWToken string `json:"JTWToken"`
RefreshToken string `json:"RefreshToken"`
}
// The file struct for Infisical config file

View File

@ -0,0 +1,82 @@
package telemetry
import (
"github.com/Infisical/infisical-merge/packages/util"
"github.com/denisbrodbeck/machineid"
"github.com/posthog/posthog-go"
"github.com/rs/zerolog/log"
)
var POSTHOG_API_KEY_FOR_CLI string
type Telemetry struct {
isEnabled bool
posthogClient posthog.Client
}
type NoOpLogger struct{}
func (NoOpLogger) Logf(format string, args ...interface{}) {
log.Debug().Msgf(format, args...)
}
func (NoOpLogger) Errorf(format string, args ...interface{}) {
log.Debug().Msgf(format, args...)
}
func NewTelemetry(telemetryIsEnabled bool) *Telemetry {
if POSTHOG_API_KEY_FOR_CLI != "" {
client, _ := posthog.NewWithConfig(
POSTHOG_API_KEY_FOR_CLI,
posthog.Config{
Logger: NoOpLogger{},
},
)
return &Telemetry{isEnabled: telemetryIsEnabled, posthogClient: client}
} else {
return &Telemetry{isEnabled: false}
}
}
func (t *Telemetry) CaptureEvent(eventName string, properties posthog.Properties) {
userIdentity, err := t.GetDistinctId()
if err != nil {
return
}
if t.isEnabled {
t.posthogClient.Enqueue(posthog.Capture{
DistinctId: userIdentity,
Event: eventName,
Properties: properties,
})
defer t.posthogClient.Close()
}
}
func (t *Telemetry) GetDistinctId() (string, error) {
var distinctId string
var outputErr error
machineId, err := machineid.ID()
if err != nil {
outputErr = err
}
infisicalConfig, err := util.GetConfigFile()
if err != nil {
outputErr = err
}
if infisicalConfig.LoggedInUserEmail != "" {
distinctId = infisicalConfig.LoggedInUserEmail
} else if machineId != "" {
distinctId = "anonymous_cli_" + machineId
} else {
distinctId = ""
}
return distinctId, outputErr
}

View File

@ -9,6 +9,7 @@ import (
"github.com/Infisical/infisical-merge/packages/config"
"github.com/Infisical/infisical-merge/packages/models"
"github.com/go-resty/resty/v2"
"github.com/rs/zerolog/log"
)
type LoggedInUserDetails struct {
@ -96,6 +97,20 @@ func GetCurrentLoggedInUserDetails() (LoggedInUserDetails, error) {
}
isAuthenticated := api.CallIsAuthenticated(httpClient)
if !isAuthenticated {
accessTokenResponse, _ := api.CallGetNewAccessTokenWithRefreshToken(httpClient, userCreds.RefreshToken)
if accessTokenResponse.Token != "" {
isAuthenticated = true
userCreds.JTWToken = accessTokenResponse.Token
}
}
err = StoreUserCredsInKeyRing(&userCreds)
if err != nil {
log.Debug().Msg("unable to store your user credentials with new access token")
}
if !isAuthenticated {
return LoggedInUserDetails{
IsUserLoggedIn: true, // was logged in

View File

@ -74,23 +74,12 @@ func ConfigContainsEmail(users []models.LoggedInUser, email string) bool {
}
func RequireLogin() {
currentUserDetails, err := GetCurrentLoggedInUserDetails()
// get the config file that stores the current logged in user email
configFile, _ := GetConfigFile()
if err != nil {
HandleError(err, "unable to retrieve your login details")
}
if !currentUserDetails.IsUserLoggedIn {
if configFile.LoggedInUserEmail == "" {
PrintErrorMessageAndExit("You must be logged in to run this command. To login, run [infisical login]")
}
if currentUserDetails.LoginExpired {
PrintErrorMessageAndExit("Your login expired, please login in again. To login, run [infisical login]")
}
if currentUserDetails.UserCredentials.Email == "" && currentUserDetails.UserCredentials.JTWToken == "" && currentUserDetails.UserCredentials.PrivateKey == "" {
PrintErrorMessageAndExit("One or more of your login details is empty. Please try logging in again via by running [infisical login]")
}
}
func RequireServiceToken() {

View File

@ -37,6 +37,8 @@ services:
- MONGO_URL=mongodb://root:example@mongo:27017/?authSource=admin
networks:
- infisical-dev
extra_hosts:
- "host.docker.internal:host-gateway"
frontend:
container_name: infisical-dev-frontend
@ -49,7 +51,6 @@ services:
volumes:
- ./frontend/src:/app/src/ # mounted whole src to avoid missing reload on new files
- ./frontend/public:/app/public
- ./frontend/next-i18next.config.js:/app/next-i18next.config.js
env_file: .env
environment:
- NEXT_PUBLIC_ENV=development

View File

@ -0,0 +1,104 @@
---
title: "scan git-changes"
description: "Scan for secrets in your uncommitted code"
---
```bash
infisical scan git-changes
# Display the full secret findings
infisical scan git-changes --verbose
```
## Description
Scanning for secrets before you commit your changes is great way to prevent leaks. Infisical makes this easy with the sub command `git-changes`.
The `git-changes` scans for uncommitted changes in a Git repository, and is especially designed for use on developer machines, aligning with the ['shift left'](https://cloud.google.com/architecture/devops/devops-tech-shifting-left-on-security) security approach.
When `git-changes` is run on a Git repository, Infisical parses the output from a `git diff` command.
To scan changes in commits that have been staged via `git add`, you can add the `--staged` flag to the sub command. This flag is particularly useful when using Infisical CLI as a pre-commit tool.
### Flags
<Accordion title="--staged">
**Description**
detect secrets in a --staged state
Default value: `false`
</Accordion>
<Accordion title="--log-opts">
**Description**
git log options
</Accordion>
<Accordion title="--baseline-path">
Short hand: `-b`
**Description**
path to baseline with issues that can be ignored
</Accordion>
<Accordion title="--config">
Short hand: `-c`
**Description**
config file path
order of precedence:
1. --config flag
2. env var INFISICAL_SCAN_CONFIG
3. (--source/-s)/.infisical-scan.toml
If none of the three options are used, then Infisical will use the default config
</Accordion>
<Accordion title="--exit-code">
**Description**
exit code when leaks have been encountered (default 1)
</Accordion>
<Accordion title="--max-target-megabytes">
**Description**
files larger than this will be skipped
</Accordion>
<Accordion title="--no-color">
**Description**
turn off color for verbose output
</Accordion>
<Accordion title="--redact">
**Description**
redact secrets from logs and stdout
</Accordion>
<Accordion title="--report-format">
**Description**
output format (json, csv, sarif) (default "json")
</Accordion>
<Accordion title="--report-path">
**Description**
report file
</Accordion>
<Accordion title="--source">
**Description**
path to source (default ".")
</Accordion>
<Accordion title="--verbose">
**Description**
show verbose output from scan
</Accordion>

View File

@ -0,0 +1,23 @@
---
title: "scan install"
description: "Add various scanning tools seamlessly into your development lifecycle"
---
```bash
infisical scan install --pre-commit-hook
```
## Description
The command `infisical scan install` is designed to incorporate various scanning tools seamlessly into your development lifecycle.
Initially, we are offering users the ability to install a pre-commit hook. This hook conducts an automatic scan for any exposed secrets in your commits before they are pushed.
### Flags
<Accordion title="--pre-commit-hook">
```bash
infisical scan install --pre-commit-hook
```
**Description**
Installs a git pre-commit hook that triggers Infisical to scan your staged changes for any exposed secrets prior to pushing.
</Accordion>

126
docs/cli/commands/scan.mdx Normal file
View File

@ -0,0 +1,126 @@
---
title: "scan"
description: "Scan git history, directories, and files for secrets"
---
```bash
infisical scan
# Display the full secret findings
infisical scan --verbose
```
## Description
The `infisical scan` command serves to scan repositories, directories, and files. It's compatible with both individual developer machines and Continuous Integration (CI) environments.
When you run `infisical scan` on a Git repository, Infisical will parses the output of a `git log -p` command. This command generates [patches](https://stackoverflow.com/questions/8279602/what-is-a-patch-in-git-version-control) that Infisical uses to identify secrets in your code.
You can configure the range of commits that `git log` will cover using the `--log-opts` flag.
Any options you can use with `git log -p` are valid for `--log-opts`.
For instance, to instruct Infisical to scan a specific range of commits, use the following command: `infisical scan --log-opts="--all commitA..commitB"`. For more details, refer to the [Git log documentation](https://git-scm.com/docs/git-log).
To scan individual files and directories, use the `--no-git` flag.
### Flags
<Accordion title="--log-opts">
**Description**
git log options
</Accordion>
<Accordion title="--no-git">
**Description**
treat git repo as a regular directory and scan those files, --log-opts has no effect on the scan when --no-git is set
Default value: `false`
</Accordion>
<Accordion title="--pipe">
Short hand: `-b`
**Description**
scan input from stdin, ex: `cat some_file | infisical scan --pipe`
Default value: `false`
</Accordion>
<Accordion title="--follow-symlinks">
Short hand: `-b`
**Description**
scan files that are symlinks to other files
Default value: `false`
</Accordion>
<Accordion title="--baseline-path">
Short hand: `-b`
**Description**
path to baseline with issues that can be ignored
</Accordion>
<Accordion title="--config">
Short hand: `-c`
**Description**
config file path
order of precedence:
1. --config flag
2. env var INFISICAL_SCAN_CONFIG
3. (--source/-s)/.infisical-scan.toml
If none of the three options are used, then Infisical will use the default config
</Accordion>
<Accordion title="--exit-code">
**Description**
exit code when leaks have been encountered (default 1)
</Accordion>
<Accordion title="--max-target-megabytes">
**Description**
files larger than this will be skipped
</Accordion>
<Accordion title="--no-color">
**Description**
turn off color for verbose output
</Accordion>
<Accordion title="--redact">
**Description**
redact secrets from logs and stdout
</Accordion>
<Accordion title="--report-format">
**Description**
output format (json, csv, sarif) (default "json")
</Accordion>
<Accordion title="--report-path">
**Description**
report file
</Accordion>
<Accordion title="--source">
**Description**
path to source (default ".")
</Accordion>
<Accordion title="--verbose">
**Description**
show verbose output from scan
</Accordion>

View File

@ -0,0 +1,244 @@
---
title: 'Secret scanning'
description: "Scan and prevent secret leaks in your code base"
---
Building upon its core functionality of fetching and injecting secrets into your applications, Infisical now takes a significant step forward in bolstering your code security.
We've enhanced our CLI tool to include a powerful scanning feature, capable of identifying more than 140 different types of secret leaks in your codebase.
In addition to scanning for past leaks, this new addition also actively aids in preventing future leaks.
# Scanning
<Tabs>
<Tab title="Scanning files, directories and or git history">
```bash
infisical scan
# Display the full secret findings
infisical scan --verbose
```
The `infisical scan` command serves to scan repositories, directories, and files. It's compatible with both individual developer machines and Continuous Integration (CI) environments.
When you run `infisical scan` on a Git repository, Infisical will parses the output of a `git log -p` command. This command generates [patches](https://stackoverflow.com/questions/8279602/what-is-a-patch-in-git-version-control) that Infisical uses to identify secrets in your code.
You can configure the range of commits that `git log` will cover using the `--log-opts` flag.
Any options you can use with `git log -p` are valid for `--log-opts`.
For instance, to instruct Infisical to scan a specific range of commits, use the following command: `infisical scan --log-opts="--all commitA..commitB"`. For more details, refer to the [Git log documentation](https://git-scm.com/docs/git-log).
To scan individual files and directories, use the `--no-git` flag.
**View [full details for this command](./commands/scan)**
</Tab>
<Tab title="Scanning uncommitted files ">
```bash
infisical scan git-changes
# Display the full secret findings
infisical git-changes --verbose
```
Scanning for secrets before you commit your changes is great way to prevent leaks. Infisical makes this easy with the sub command `git-changes`.
The `git-changes` scans for uncommitted changes in a Git repository, and is especially designed for use on developer machines, aligning with the ['shift left'](https://cloud.google.com/architecture/devops/devops-tech-shifting-left-on-security) security approach.
When `git-changes` is run on a Git repository, Infisical parses the output from a `git diff` command.
To scan changes in commits that have been staged via `git add`, you can add the `--staged` flag to the sub command. This flag is particularly useful when using Infisical CLI as a pre-commit tool.
**View [full details for this command](./commands/scan-git-changes)**
<Info>
`git-changes` command is only for Git repositories; using it on files or directories will result in an error.
</Info>
</Tab>
</Tabs>
#
#
# Automatically scan changes before you commit
To lower the risk of committing hardcoded secrets to your code repository, we have designed a custom git pre-commit hook.
This hook scans the changes you're about to commit for any exposed secrets. If any hardcoded secrets are detected, it will block your commit.
### Install pre-commit hook
To install this git hook, go into your local git repository and run the following command.
```bash
infisical scan install --pre-commit-hook
```
To disable this hook after installing it, run the command `git config --bool hooks.infisical-scan false`
### Third party hooks management
If you prefer to manage your pre-commit hook outside of the .git/hooks directory, you can easily accomplish this by adding the following command to your pre-commit script
```bash
infisical scan git-changes --staged --verbose
```
#
#
# Creating a baseline
When scanning large repositories or repositories with a long history, it can be helpful to use a baseline.
A baseline allows Infisical to ignore any old findings that are already present in the baseline findings. You can create a infisical scan report by running `infisical scan` with the `--report-path` flag.
To create a Infisical scan report and save it in a file called leaks-report.json, use the following command:
```
infisical scan --report-path leaks-report.json
```
Once a baseline is created, you can apply it when running the `infisical scan` command again. Use the following command:
```
infisical scan --baseline-path leaks-report.json --report-path findings.json
```
After running the `scan` command with the `--baseline-path` flag, the report output in findings.json will only contain new issues.
#
#
# Configuration file
To customize the scan, such as specifying your own rules or establishing exceptions for certain files or paths that should not be flagged as risks, you can define these specifications in the configuration file.
<Accordion title="Example custom configuration file">
```toml infisical-scan.toml
# Title for the configuration file
title = "Some title"
# This configuration is the foundation that can be expanded. If there are any overlapping rules
# between this base and the expanded configuration, the rules in this base will take priority.
# Another aspect of extending configurations is the ability to link multiple files, up to a depth of 2.
# "Allowlist" arrays get appended and may have repeated elements.
# "useDefault" and "path" cannot be used simultaneously. Please choose one.
[extend]
# useDefault will extend the base configuration with the default config:
# https://raw.githubusercontent.com/Infisical/infisical/main/cli/config/infisical-scan.toml
useDefault = true
# or you can supply a path to a configuration. Path is relative to where infisical cli
# was invoked, not the location of the base config.
path = "common_config.toml"
# An array of tables that contain information that define instructions
# on how to detect secrets
[[rules]]
# Unique identifier for this rule
id = "some-identifier-for-rule"
# Short human readable description of the rule.
description = "awesome rule 1"
# Golang regular expression used to detect secrets. Note Golang's regex engine
# does not support lookaheads.
regex = '''one-go-style-regex-for-this-rule'''
# Golang regular expression used to match paths. This can be used as a standalone rule or it can be used
# in conjunction with a valid `regex` entry.
path = '''a-file-path-regex'''
# Array of strings used for metadata and reporting purposes.
tags = ["tag","another tag"]
# A regex match may have many groups, this allows you to specify the group that should be used as (which group the secret is contained in)
# its entropy checked if `entropy` is set.
secretGroup = 3
# Float representing the minimum shannon entropy a regex group must have to be considered a secret.
# Shannon entropy measures how random a data is. Since secrets are usually composed of many random characters, they typically have high entropy
entropy = 3.5
# Keywords are used for pre-regex check filtering.
# If rule has keywords but the text fragment being scanned doesn't have at least one of it's keywords, it will be skipped for processing further.
# Ideally these values should either be part of the idenitifer or unique strings specific to the rule's regex
# (introduced in v8.6.0)
keywords = [
"auth",
"password",
"token",
]
# You can include an allowlist table for a single rule to reduce false positives or ignore commits
# with known/rotated secrets
[rules.allowlist]
description = "ignore commit A"
commits = [ "commit-A", "commit-B"]
paths = [
'''go\.mod''',
'''go\.sum'''
]
# note: (rule) regexTarget defaults to check the _Secret_ in the finding.
# if regexTarget is not specified then _Secret_ will be used.
# Acceptable values for regexTarget are "match" and "line"
regexTarget = "match"
regexes = [
'''process''',
'''getenv''',
]
# note: stopwords targets the extracted secret, not the entire regex match
# if the extracted secret is found in the stopwords list, the finding will be skipped (i.e not included in report)
stopwords = [
'''client''',
'''endpoint''',
]
# This is a global allowlist which has a higher order of precedence than rule-specific allowlists.
# If a commit listed in the `commits` field below is encountered then that commit will be skipped and no
# secrets will be detected for said commit. The same logic applies for regexes and paths.
[allowlist]
description = "global allow list"
commits = [ "commit-A", "commit-B", "commit-C"]
paths = [
'''gitleaks\.toml''',
'''(.*?)(jpg|gif|doc)'''
]
# note: (global) regexTarget defaults to check the _Secret_ in the finding.
# if regexTarget is not specified then _Secret_ will be used.
# Acceptable values for regexTarget are "match" and "line"
regexTarget = "match"
regexes = [
'''219-09-9999''',
'''078-05-1120''',
'''(9[0-9]{2}|666)-\d{2}-\d{4}''',
]
# note: stopwords targets the extracted secret, not the entire regex match
# if the extracted secret is found in the stopwords list, the finding will be skipped (i.e not included in report)
stopwords = [
'''client''',
'''endpoint''',
]
```
</Accordion>
#
#
# Ignoring Known Secrets
If you're intentionally committing a test secret that `infisical scan` might flag, you can instruct Infisical to overlook that secret with the methods listed below.
### infisical-scan:ignore
To ignore a secret contained in line of code, simply add `infisical-scan:ignore ` at the end of the line as comment in the given programming.
```js example.js
function helloWorld() {
console.log("8dyfuiRyq=vVc3RRr_edRk-fK__JItpZ"); // infisical-scan:ignore
}
```
### .infisicalignore
An alternative method to exclude specific findings involves creating a .infisicalignore file at your repository's root.
You can then add the fingerprints of the findings you wish to exclude. The Infisical scan report provides a unique Fingerprint for each secret found.
By incorporating these Fingerprints into the .infisicalignore file, Infisical will skip the corresponding secret findings in subsequent scans.
```.ignore .infisicalignore
bea0ff6e05a4de73a5db625d4ae181a015b50855:frontend/components/utilities/attemptLogin.js:stripe-access-token:147
bea0ff6e05a4de73a5db625d4ae181a015b50855:backend/src/json/integrations.json:generic-api-key:5
1961b92340e5d2613acae528b886c842427ce5d0:frontend/components/utilities/attemptLogin.js:stripe-access-token:148
```

View File

@ -34,10 +34,29 @@ infisical init
## Inject environment variables
```bash
# inject environment variables into app
infisical run -- [your application start command]
```
<Accordion title="Injecting environment variables directly" defaultOpen="true">
```bash
# inject environment variables into app
infisical run -- [your application start command]
```
</Accordion>
<Accordion title="Injecting environment variables in custom aliases">
Custom aliases can utilize secrets from Infisical. Suppose there is a custom alias `yd` in `custom.sh` that runs `yarn dev` and needs the secrets provided by Infisical.
```bash
#!/bin/sh
yd() {
yarn dev
}
```
To make the secrets available from Infisical to `yd`, you can run the following command:
```bash
infisical run --command="source custom.sh && yd"
```
</Accordion>
View all available options for `run` command [here](./commands/run)

View File

@ -79,6 +79,13 @@ Start syncing environment variables with [Infisical Cloud](https://app.infisical
>
Explore integrations for Next.js, Express, Django, and more
</Card>
<Card
href="/cli/scanning-overview"
title="Secret scanning"
color="#0285c7"
>
Scan and prevent 140+ secret type leaks in your codebase
</Card>
<Card
href="https://calendly.com/team-infisical/infisical-demo"
title="Contact Us"

View File

@ -140,7 +140,7 @@
"cli/overview",
"cli/usage",
{
"group": "Commands",
"group": "Core commands",
"pages": [
"cli/commands/login",
"cli/commands/init",
@ -149,9 +149,18 @@
"cli/commands/export",
"cli/commands/vault",
"cli/commands/user",
"cli/commands/reset"
"cli/commands/reset",
{
"group": "infisical scan",
"pages": [
"cli/commands/scan",
"cli/commands/scan-git-changes",
"cli/commands/scan-install"
]
}
]
},
"cli/scanning-overview",
"cli/project-config",
"cli/faq"
]

View File

@ -21,6 +21,11 @@ docker pull infisical/infisical:latest
The Infisical Docker image requires a .env file to manage environment variables.
Create a new file called .env in your preferred location. Add the required environment variables listed below. View [all configurable environment variables](../configuration/envars)
<ParamField query="ENCRYPTION_KEY" type="string" default="none" required>
Must be a random 16 byte hex string. Can be generated with `openssl rand -hex 16`
</ParamField>
<ParamField query="JWT_SIGNUP_SECRET" type="string" default="none" required>
Must be a random 16 byte hex string. Can be generated with `openssl rand -hex 16`
</ParamField>

View File

@ -1,5 +1,5 @@
{
"name": "npm-proj-1682714485428-0.1455767275640205qNcC1I",
"name": "npm-proj-1684411062139-0.5362546945149991xa7WAK",
"lockfileVersion": 2,
"requires": true,
"packages": {
@ -45,7 +45,7 @@
"cookies": "^0.8.0",
"cva": "npm:class-variance-authority@^0.4.0",
"framer-motion": "^6.2.3",
"fs": "^0.0.1-security",
"fs": "^0.0.2",
"gray-matter": "^4.0.3",
"http-proxy": "^1.18.1",
"i18next": "^22.4.15",
@ -57,7 +57,7 @@
"lottie-react": "^2.4.0",
"markdown-it": "^13.0.1",
"next": "^12.3.4",
"posthog-js": "^1.39.4",
"posthog-js": "^1.54.0",
"query-string": "^7.1.3",
"react": "^17.0.2",
"react-beautiful-dnd": "^13.1.1",
@ -12767,9 +12767,9 @@
}
},
"node_modules/fs": {
"version": "0.0.1-security",
"resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
"integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w=="
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/fs/-/fs-0.0.2.tgz",
"integrity": "sha512-YAiVokMCrSIFZiroB1oz51hPiPRVcUtSa4x2U5RYXyhS9VAPdiFigKbPTnOSq7XY8wd3FIVPYmXpo5lMzFmxgg=="
},
"node_modules/fs-constants": {
"version": "1.0.0",
@ -17460,9 +17460,9 @@
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
},
"node_modules/posthog-js": {
"version": "1.53.4",
"resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.53.4.tgz",
"integrity": "sha512-aaQ9S+/eDuBl2XTuU/lMyMtX7eeNAQ/+53O0O+I05FwX7e5NDN1nVqlnkMP0pfZlFcnsPaVqm8N3HoYj+b7Eow==",
"version": "1.54.0",
"resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.54.0.tgz",
"integrity": "sha512-5kT9zvmBrCshFkWqOrYxk13Y/2j7vtV0sF3XmQa5QDD0bVM1KGU4blyZySBt8SAcO1z3WsKvG4bFGxS7TPqjjw==",
"dependencies": {
"fflate": "^0.4.1",
"rrweb-snapshot": "^1.1.14"
@ -31436,9 +31436,9 @@
"dev": true
},
"fs": {
"version": "0.0.1-security",
"resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
"integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w=="
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/fs/-/fs-0.0.2.tgz",
"integrity": "sha512-YAiVokMCrSIFZiroB1oz51hPiPRVcUtSa4x2U5RYXyhS9VAPdiFigKbPTnOSq7XY8wd3FIVPYmXpo5lMzFmxgg=="
},
"fs-constants": {
"version": "1.0.0",
@ -34822,9 +34822,9 @@
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
},
"posthog-js": {
"version": "1.53.4",
"resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.53.4.tgz",
"integrity": "sha512-aaQ9S+/eDuBl2XTuU/lMyMtX7eeNAQ/+53O0O+I05FwX7e5NDN1nVqlnkMP0pfZlFcnsPaVqm8N3HoYj+b7Eow==",
"version": "1.54.0",
"resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.54.0.tgz",
"integrity": "sha512-5kT9zvmBrCshFkWqOrYxk13Y/2j7vtV0sF3XmQa5QDD0bVM1KGU4blyZySBt8SAcO1z3WsKvG4bFGxS7TPqjjw==",
"requires": {
"fflate": "^0.4.1",
"rrweb-snapshot": "^1.1.14"

View File

@ -52,7 +52,7 @@
"cookies": "^0.8.0",
"cva": "npm:class-variance-authority@^0.4.0",
"framer-motion": "^6.2.3",
"fs": "^0.0.1-security",
"fs": "^0.0.2",
"gray-matter": "^4.0.3",
"http-proxy": "^1.18.1",
"i18next": "^22.4.15",
@ -64,7 +64,7 @@
"lottie-react": "^2.4.0",
"markdown-it": "^13.0.1",
"next": "^12.3.4",
"posthog-js": "^1.39.4",
"posthog-js": "^1.54.0",
"query-string": "^7.1.3",
"react": "^17.0.2",
"react-beautiful-dnd": "^13.1.1",

View File

@ -17,8 +17,8 @@
"starter": {
"name": "Starter",
"price-explanation": "up to 5 team members",
"text": "Manage any project with 5 members for free!",
"subtext": "$5 per member/month afterwards."
"text": "Manage up to 3 project with up to 5 team members for free!",
"subtext": ""
},
"professional": {
"name": "Professional",

View File

@ -285,7 +285,7 @@ const ProjectUsersTable = ({ userData, changeData, myUser, filter }: Props) => {
>
<Select
className="w-16 bg-mineshaft-700"
dropdownContainerClassName="bg-mineshaft-700"
dropdownContainerClassName="bg-mineshaft-800 border border-mineshaft-600 text-bunker-200"
position="item-aligned"
// open={isOpen}
onValueChange={(val) =>

View File

@ -20,8 +20,8 @@ interface Props {
export default function Plan({ plan }: Props) {
return (
<div
className={`relative flex flex-col justify-between border-2 min-w-fit w-96 rounded-lg h-68 mr-4 bg-mineshaft-800 ${
plan.name !== 'Starter' && plan.current === true ? 'border-primary' : 'border-chicago-700'
className={`relative flex flex-col justify-between border min-w-fit w-96 rounded-lg h-68 mr-4 bg-mineshaft-800 ${
plan.name !== 'Starter' && plan.current === true ? 'border-2 border-primary' : 'border-mineshaft-600'
}
`}
>
@ -41,13 +41,13 @@ export default function Plan({ plan }: Props) {
<>
{plan.buttonTextMain === 'Schedule a Demo' ? (
<a href="/scheduledemo" target='_blank rel="noopener"'>
<div className="relative z-10 mx-5 mt-3 mb-4 py-2 px-4 border border-1 border-gray-600 hover:text-black hover:border-primary text-gray-400 font-semibold hover:bg-primary bg-bunker duration-200 cursor-pointer rounded-md flex w-max">
<div className="relative z-10 mx-5 mt-3 mb-4 py-2 px-4 border border-1 border-mineshaft-600 hover:text-black hover:border-primary text-gray-400 font-semibold hover:bg-primary bg-bunker duration-200 cursor-pointer rounded-md flex w-max">
{plan.buttonTextMain}
</div>
</a>
) : (
<div
className={`relative z-10 mx-5 mt-3 mb-4 py-2 px-4 border border-1 border-gray-600 text-gray-400 font-semibold ${
className={`relative z-10 mx-5 mt-3 mb-4 py-2 px-4 border border-1 border-mineshaft-600 text-gray-400 font-semibold ${
plan.buttonTextMain === 'Downgrade'
? 'hover:bg-red hover:text-white hover:border-red'
: 'hover:bg-primary hover:text-black hover:border-primary'
@ -74,7 +74,7 @@ export default function Plan({ plan }: Props) {
) : (
<div
className={`h-8 w-full rounded-b-md flex justify-center items-center z-10 ${
plan.name !== 'Starter' && plan.current === true ? 'bg-primary' : 'bg-chicago-700'
plan.name !== 'Starter' && plan.current === true ? 'bg-primary' : 'bg-mineshaft-400'
}`}
>
<p className="text-xs text-black font-semibold">CURRENT PLAN</p>

View File

@ -191,6 +191,17 @@ const IntegrationTile = ({
/>
</div>
);
case 'gitlab':
return (
<div>
<div className="mb-2 text-xs font-semibold text-gray-400">ENVIRONMENT</div>
<ListBox
data={null}
isSelected={integration.targetEnvironment}
onChange={setIntegrationTargetEnvironment}
/>
</div>
);
default:
return <div />;
}

View File

@ -24,7 +24,7 @@ const buttonVariants = cva(
{
variants: {
colorSchema: {
primary: ['bg-primary', 'text-black', 'border-primary bg-opacity-80 hover:bg-opacity-100'],
primary: ['bg-primary', 'text-black', 'border-primary bg-opacity-90 hover:bg-opacity-100'],
secondary: ['bg-mineshaft', 'text-gray-300', 'border-mineshaft hover:bg-opacity-80'],
danger: ['bg-red', 'text-white', 'border-red hover:bg-opacity-90'],
gray: ['bg-bunker-500', 'text-bunker-200']
@ -40,7 +40,7 @@ const buttonVariants = cva(
},
isDisabled: {
true: 'bg-mineshaft-700 border border-mineshaft-600 text-white opacity-50 cursor-not-allowed',
false: 'border border-primary-400'
false: 'border'
},
isFullWidth: {
true: 'w-full',
@ -111,7 +111,7 @@ const buttonVariants = cva(
{
colorSchema: 'secondary',
variant: 'plain',
className: 'text-mineshaft-300'
className: 'text-mineshaft-300 hover:text-mineshaft-200 border-none'
},
{
colorSchema: 'danger',

View File

@ -18,7 +18,7 @@ export const UpgradePlanModal = ({ text, isOpen, onOpenChange }: Props): JSX.Ele
href={`/settings/billing/${localStorage.getItem('projectData.id') as string}`}
key="upgrade-plan"
>
<Button className="mr-4">Upgrade Plan</Button>
<Button className="mr-4 ml-2">Upgrade Plan</Button>
</Link>,
<ModalClose asChild key="upgrade-plan-cancel">
<Button colorSchema="secondary" variant="plain">
@ -27,8 +27,8 @@ export const UpgradePlanModal = ({ text, isOpen, onOpenChange }: Props): JSX.Ele
</ModalClose>
]}
>
<p className="mb-4 text-bunker-300">{text}</p>
<p className="font-medium text-bunker-300">
<p className="mb-2 text-bunker-300">{text}</p>
<p className="text-bunker-300">
Upgrade and get access to this, as well as to other powerful enhancements.
</p>
</ModalContent>

View File

@ -30,7 +30,7 @@ interface Mapping {
}
const plansDev: Mapping = {
starter: 'prod_Mb4ATFT5QAHoPM',
starter: 'prod_Nt6kPvYsVBuzVH',
team: 'prod_NEpD2WMXUS2eDn',
professional: 'prod_Mb4CetZ2jE7jdl',
enterprise: 'licence_key_required'

View File

@ -5,7 +5,7 @@ import { apiRequest } from '@app/config/request';
import { UploadWsKeyDTO, UserWsKeyPair } from './types';
const encKeyKeys = {
getUserWorkspaceKey: (workspaceID: string) => ['worksapce-key-pair', { workspaceID }] as const
getUserWorkspaceKey: (workspaceID: string) => ['workspace-key-pair', { workspaceID }] as const
};
const fetchUserWsKey = async (workspaceID: string) => {

View File

@ -28,9 +28,11 @@ import {
Modal,
ModalContent,
Select,
SelectItem
SelectItem,
UpgradePlanModal
} from '@app/components/v2';
import { useOrganization, useUser, useWorkspace } from '@app/context';
import { plans } from '@app/const';
import { useOrganization, useSubscription, useUser, useWorkspace } from '@app/context';
import { usePopUp } from '@app/hooks';
import { fetchOrgUsers, useAddUserToWs, useCreateWorkspace, useUploadWsKey } from '@app/hooks/api';
import getOrganizations from '@app/pages/api/organization/getOrgs';
@ -57,13 +59,18 @@ export const AppLayout = ({ children }: LayoutProps) => {
const { workspaces, currentWorkspace } = useWorkspace();
const { currentOrg } = useOrganization();
const { user } = useUser();
const { subscriptionPlan } = useSubscription();
const host = window.location.origin;
const isAddingProjectsAllowed =
subscriptionPlan !== plans.starter || (subscriptionPlan === plans.starter && workspaces.length < 3) || host !== 'https://app.infisical.com';
const createWs = useCreateWorkspace();
const uploadWsKey = useUploadWsKey();
const addWsUser = useAddUserToWs();
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
'addNewWs'
'addNewWs',
'upgradePlan'
] as const);
const {
control,
@ -241,7 +248,7 @@ export const AppLayout = ({ children }: LayoutProps) => {
<aside className="w-full border-r border-mineshaft-600 bg-gradient-to-tr from-mineshaft-700 via-mineshaft-800 to-mineshaft-900 md:w-60">
<nav className="items-between flex h-full flex-col justify-between">
<div>
{currentWorkspace ? (
{currentWorkspace && router.asPath !== "/noprojects" ? (
<div className="mt-3 mb-4 w-full p-4">
<p className="ml-1.5 mb-1 text-xs font-semibold uppercase text-gray-400">
Project
@ -274,7 +281,13 @@ export const AppLayout = ({ children }: LayoutProps) => {
colorSchema="primary"
variant="outline_bg"
size="sm"
onClick={() => handlePopUpOpen('addNewWs')}
onClick={() => {
if (isAddingProjectsAllowed) {
handlePopUpOpen('addNewWs')
} else {
handlePopUpOpen('upgradePlan');
}
}}
leftIcon={<FontAwesomeIcon icon={faPlus} />}
>
Add Project
@ -285,17 +298,25 @@ export const AppLayout = ({ children }: LayoutProps) => {
) : (
<div className="mt-3 mb-4 w-full p-4">
<Button
className="w-full bg-mineshaft-500 py-2 text-bunker-200 hover:bg-primary/90 hover:text-black"
color="mineshaft"
className="border-mineshaft-500"
colorSchema="primary"
variant="outline_bg"
size="sm"
onClick={() => handlePopUpOpen('addNewWs')}
isFullWidth
onClick={() => {
if (isAddingProjectsAllowed) {
handlePopUpOpen('addNewWs')
} else {
handlePopUpOpen('upgradePlan');
}
}}
leftIcon={<FontAwesomeIcon icon={faPlus} />}
>
Add Project
</Button>
</div>
)}
<div className={`${currentWorkspace ? 'block' : 'hidden'}`}>
<div className={`${currentWorkspace && router.asPath !== "/noprojects" ? 'block' : 'hidden'}`}>
<Menu>
<Link href={`/dashboard/${currentWorkspace?._id}`} passHref>
<a>
@ -464,6 +485,11 @@ export const AppLayout = ({ children }: LayoutProps) => {
</form>
</ModalContent>
</Modal>
<UpgradePlanModal
isOpen={popUp.upgradePlan.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle('upgradePlan', isOpen)}
text="You have exceeded the number of projects allowed on the free plan."
/>
<main className="flex-1 overflow-y-auto overflow-x-hidden bg-bunker-800 dark:[color-scheme:dark]">
{children}
</main>

View File

@ -2,7 +2,15 @@ import { useEffect, useState } from 'react';
import { useRouter } from 'next/router';
import queryString from 'query-string';
import { Button, Card, CardTitle, FormControl, Select, SelectItem } from '../../../components/v2';
import {
Button,
Card,
CardTitle,
FormControl,
Input,
Select,
SelectItem
} from '../../../components/v2';
import {
useGetIntegrationAuthApps,
useGetIntegrationAuthById,
@ -37,6 +45,7 @@ export default function GitLabCreateIntegrationPage() {
const [targetEntity, setTargetEntity] = useState(gitLabEntities[0].value);
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState('');
const [targetAppId, setTargetAppId] = useState('');
const [targetEnvironment, setTargetEnvironment] = useState('');
const [isLoading, setIsLoading] = useState(false);
@ -86,7 +95,7 @@ export default function GitLabCreateIntegrationPage() {
)?.name ?? null,
appId: targetAppId,
sourceEnvironment: selectedSourceEnvironment,
targetEnvironment: null,
targetEnvironment: targetEnvironment === '' ? '*' : targetEnvironment,
targetEnvironmentId: null,
targetService: null,
targetServiceId: null,
@ -189,6 +198,15 @@ export default function GitLabCreateIntegrationPage() {
)}
</Select>
</FormControl>
<FormControl
label="GitLab Environment Scope (Optional)"
>
<Input
placeholder="*"
value={targetEnvironment}
onChange={(e) => setTargetEnvironment(e.target.value)}
/>
</FormControl>
<Button
onClick={handleButtonClick}
color="mineshaft"

View File

@ -1,4 +1,5 @@
import { useEffect } from 'react';
import Head from 'next/head';
import Image from 'next/image';
import { useRouter } from 'next/router';
@ -27,10 +28,15 @@ export default function NoProjects() {
return (
<div className="mr-auto flex h-full w-11/12 flex-col items-center justify-center text-center text-lg text-gray-300">
<Head>
<title>No Projects | Infisical</title>
<link rel="icon" href="/infisical.ico" />
<meta property="og:image" content="/images/message.png" />
</Head>
<div className="mb-6 mt-16 mr-16">
<Image src="/images/dragon-cant-find.svg" height={270} width={436} alt="google logo" />
</div>
<div className="mb-8 rounded-md bg-bunker-500 px-4 py-6 text-bunker-300 shadow-xl">
<div className="mb-8 rounded-md bg-mineshaft-900 border border-mineshaft-700 px-4 py-6 text-bunker-300 shadow-xl">
<div className="max-w-md">
You are not part of any projects in this organization yet. When you are, they will appear
here.

View File

@ -1,10 +1,10 @@
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import Head from 'next/head';
import { plans as plansConstant } from 'public/data/frequentConstants';
import Plan from '@app/components/billing/Plan';
import NavHeader from '@app/components/navigation/NavHeader';
import { plans as plansConstant } from '@app/const';
import getOrganizationSubscriptions from '../../api/organization/GetOrgSubscription';
import getOrganizationUsers from '../../api/organization/GetOrgUsers';
@ -32,7 +32,7 @@ export default function SettingsBilling() {
name: 'Team',
price: '$8',
priceExplanation: t('billing.professional.price-explanation')!,
text: 'For teams that want to improve their efficiency and security.',
text: 'Unlimited members, up to 10 projects. Additional developer experience features.',
buttonTextMain: t('billing.upgrade')!,
buttonTextSecondary: t('billing.learn-more')!,
current: currentPlan === plansConstant.team

View File

@ -505,7 +505,7 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
<IconButton
ariaLabel="recovery"
variant="outline_bg"
onClick={() => setIsSecretValueHidden.toggle()}
onClick={() => handlePopUpOpen('secretSnapshots')}
>
<FontAwesomeIcon icon={faCodeCommit} />
</IconButton>
@ -530,7 +530,15 @@ export const DashboardPage = ({ envFromTop }: { envFromTop: string }) => {
<IconButton
ariaLabel="recovery"
variant="outline_bg"
onClick={() => setIsSecretValueHidden.toggle()}
onClick={() => {
if (secretContainer.current) {
secretContainer.current.scroll({
top: 0,
behavior: 'smooth'
});
}
prepend(DEFAULT_SECRET_VALUE, { shouldFocus: false });
}}
>
<FontAwesomeIcon icon={faPlus} />
</IconButton>

View File

@ -184,6 +184,7 @@ export const OrgMembersTable = ({
defaultValue={role}
isDisabled={userId === user?._id}
className="w-40 bg-mineshaft-600"
dropdownContainerClassName="border border-mineshaft-600 bg-mineshaft-800"
onValueChange={(selectedRole) =>
onRoleChange(orgMembershipId, selectedRole)
}
@ -196,7 +197,7 @@ export const OrgMembersTable = ({
</Select>
)}
{((status === 'invited' || status === 'verified') && serverDetails?.emailConfigured) && (
<Button className='w-40' colorSchema="secondary" onClick={() => onInviteMember(email)}>
<Button className='w-40' colorSchema="primary" variant="outline_bg" onClick={() => onInviteMember(email)}>
Resend Invite
</Button>
)}
@ -218,8 +219,10 @@ export const OrgMembersTable = ({
))
) : (
<div className='flex flex-row'>
<Tag colorSchema="red">This user isn&apos;t part of any projects yet</Tag>
{router.query.id !== 'undefined' && <button
{((status === 'invited' || status === 'verified') && serverDetails?.emailConfigured)
? <Tag colorSchema="red">This user hasn&apos;t accepted the invite yet</Tag>
: <Tag colorSchema="red">This user isn&apos;t part of any projects yet</Tag>}
{router.query.id !== 'undefined' && !((status === 'invited' || status === 'verified') && serverDetails?.emailConfigured) && <button
type="button"
onClick={() => router.push(`/users/${router.query.id}`)}
className='text-sm bg-mineshaft w-max px-1.5 py-0.5 hover:bg-primary duration-200 hover:text-black cursor-pointer rounded-sm'

View File

@ -335,15 +335,11 @@ export const OrgServiceAccountsTable = () => {
);
})
)}
{!isServiceAccountsLoading && filteredServiceAccounts?.length === 0 && (
<Tr>
<Td colSpan={4} className="text-center">
<EmptyState title="No service accounts found" icon={faServer} />
</Td>
</Tr>
)}
</TBody>
</Table>
{!isServiceAccountsLoading && filteredServiceAccounts?.length === 0 && (
<EmptyState title="No service accounts found" icon={faServer} />
)}
</TableContainer>
<Modal
isOpen={popUp?.addServiceAccount?.isOpen}

View File

@ -11,7 +11,7 @@ export const ProjectIndexSecretsSection = ({
onEnableBlindIndices
}: Props) => {
return (
<div className="rounded-md bg-white/5 p-6">
<div className="rounded-md bg-white/5 p-6 my-2">
<p className="mb-4 text-xl font-semibold">Blind Indices</p>
<p className="mb-4 text-sm text-gray-400">
Your project, created before the introduction of blind indexing, contains unindexed secrets. To access individual secrets by name through the SDK and public API, please enable blind indexing.