From d47f6f7ec999a6fc3c2e9ef1f731ae3abb2d8bd5 Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Tue, 29 Jul 2025 20:49:54 +0800 Subject: [PATCH 1/2] misc: removed CLI directory --- cli/.gitignore | 4 - cli/.infisicalignore | 3 - cli/agent-config.yaml | 37 - cli/detect/baseline.go | 103 - cli/detect/cmd/scm/scm.go | 70 - cli/detect/config/allowlist.go | 159 - cli/detect/config/config.go | 426 --- cli/detect/config/gitleaks.toml | 3130 ----------------- cli/detect/config/rule.go | 114 - cli/detect/config/utils.go | 46 - cli/detect/decoder.go | 328 -- cli/detect/detect.go | 699 ---- cli/detect/directory.go | 225 -- cli/detect/git.go | 216 -- cli/detect/location.go | 102 - cli/detect/logging/log.go | 72 - cli/detect/reader.go | 149 - cli/detect/regexp/stdlib_regex.go | 37 - cli/detect/regexp/wasilibs_regex.go | 37 - cli/detect/report/constants.go | 26 - cli/detect/report/csv.go | 100 - cli/detect/report/finding.go | 92 - cli/detect/report/json.go | 39 - cli/detect/report/junit.go | 129 - cli/detect/report/report.go | 38 - cli/detect/report/sarif.go | 239 -- cli/detect/report/template.go | 68 - cli/detect/sources/directory.go | 127 - cli/detect/sources/git.go | 211 -- cli/detect/utils.go | 280 -- cli/docker/alpine | 9 - cli/go.mod | 183 - cli/go.sum | 951 ----- cli/goreleaser.dockerfile | 4 - cli/infisical-cli.repo | 5 - cli/main.go | 17 - cli/packages/api/api.go | 652 ---- cli/packages/api/errors.go | 80 - cli/packages/api/model.go | 689 ---- cli/packages/cmd/agent.go | 1081 ------ cli/packages/cmd/bootstrap.go | 277 -- cli/packages/cmd/cmd_test.go | 49 - cli/packages/cmd/dynamic_secrets.go | 676 ---- cli/packages/cmd/export.go | 240 -- cli/packages/cmd/export_test.go | 79 - cli/packages/cmd/folder.go | 209 -- cli/packages/cmd/gateway.go | 318 -- cli/packages/cmd/init.go | 195 - cli/packages/cmd/kmip.go | 103 - cli/packages/cmd/login.go | 1022 ------ cli/packages/cmd/man.go | 35 - .../pre-commit-without-bang.sh | 20 - .../cmd/pre-commit-script/pre-commit.sh | 20 - cli/packages/cmd/reset.go | 45 - cli/packages/cmd/root.go | 104 - cli/packages/cmd/run.go | 491 --- cli/packages/cmd/scan.go | 636 ---- cli/packages/cmd/secrets.go | 782 ---- cli/packages/cmd/ssh.go | 1142 ------ cli/packages/cmd/token.go | 63 - cli/packages/cmd/tokens.go | 193 - cli/packages/cmd/user.go | 325 -- cli/packages/cmd/vault.go | 113 - cli/packages/config/config.go | 5 - cli/packages/crypto/crypto.go | 86 - cli/packages/gateway/connection.go | 358 -- cli/packages/gateway/constants.go | 17 - cli/packages/gateway/gateway.go | 371 -- cli/packages/gateway/relay.go | 188 - cli/packages/gateway/relay_windows.go | 37 - cli/packages/gateway/systemd.go | 121 - .../gateway/udp_listener/listener_unix.go | 26 - .../gateway/udp_listener/listener_windows.go | 18 - cli/packages/models/cli.go | 161 - cli/packages/srp/client.go | 140 - cli/packages/srp/params.go | 95 - cli/packages/srp/server.go | 104 - cli/packages/srp/srp.go | 103 - cli/packages/srp/util.go | 48 - cli/packages/systemd/daemon.go | 84 - cli/packages/telemetry/telemetry.go | 82 - cli/packages/util/agent.go | 41 - cli/packages/util/auth.go | 208 -- cli/packages/util/check-for-update.go | 183 - cli/packages/util/common.go | 117 - cli/packages/util/config.go | 259 -- cli/packages/util/constants.go | 63 - cli/packages/util/credentials.go | 127 - cli/packages/util/exec.go | 92 - cli/packages/util/folders.go | 281 -- cli/packages/util/helper.go | 338 -- cli/packages/util/init.go | 46 - cli/packages/util/keyringwrapper.go | 69 - cli/packages/util/log.go | 49 - cli/packages/util/secrets.go | 824 ----- .../util/testdata/infisical-branch-env.json | 7 - .../util/testdata/infisical-default-env.json | 5 - .../infisical-no-matching-branch-env.json | 7 - cli/packages/util/vault.go | 22 - .../visualize/dynamic_secret_leases.go | 39 - cli/packages/visualize/folders.go | 14 - cli/packages/visualize/secrets.go | 14 - cli/packages/visualize/visualize.go | 134 - cli/scripts/completions.sh | 8 - cli/scripts/export_test_env.sh | 23 - cli/scripts/install.sh | 97 - cli/scripts/manpages.sh | 6 - cli/scripts/setup.deb.sh | 551 --- cli/secret-render-template | 5 - ...-TestServiceToken_ExportSecretsWithImports | 5 - ...stServiceToken_ExportSecretsWithoutImports | 3 - ...TestServiceToken_GetSecretsByNameRecursive | 7 - ...stServiceToken_GetSecretsByNameWithImports | 7 - ...ceToken_GetSecretsByNameWithNotFoundSecret | 8 - ...TestServiceToken_RunCmdRecursiveAndImports | 2 - .../test-TestServiceToken_RunCmdWithImports | 2 - ...test-TestServiceToken_RunCmdWithoutImports | 2 - ...Token_SecretsGetWithImportsAndRecursiveCmd | 10 - ...etsGetWithoutImportsAndWithoutRecursiveCmd | 7 - ...TestUniversalAuth_ExportSecretsWithImports | 5 - ...tUniversalAuth_ExportSecretsWithoutImports | 3 - ...estUniversalAuth_GetSecretsByNameRecursive | 7 - ...tUniversalAuth_GetSecretsByNameWithImports | 7 - ...salAuth_GetSecretsByNameWithNotFoundSecret | 8 - ...estUniversalAuth_RunCmdRecursiveAndImports | 2 - .../test-TestUniversalAuth_RunCmdWithImports | 2 - ...est-TestUniversalAuth_RunCmdWithoutImports | 2 - ...lAuth_SecretsGetWithImportsAndRecursiveCmd | 10 - ...etsGetWithoutImportsAndWithoutRecursiveCmd | 7 - ...stUniversalAuth_SecretsGetWrongEnvironment | 4 - .../test-TestUserAuth_SecretsGetAll | 7 - ...estUserAuth_SecretsGetAllWithoutConnection | 8 - cli/test/export_test.go | 66 - cli/test/helper.go | 107 - cli/test/login_test.go | 139 - cli/test/main_test.go | 23 - cli/test/run_test.go | 108 - cli/test/secrets_by_name_test.go | 94 - cli/test/secrets_test.go | 123 - cli/testdata/baseline/baseline.csv | 2 - cli/testdata/baseline/baseline.json | 40 - cli/testdata/baseline/baseline.sarif | 6 - cli/testdata/config/allow_aws_re.toml | 9 - cli/testdata/config/allow_commit.toml | 9 - cli/testdata/config/allow_global_aws_re.toml | 8 - cli/testdata/config/allow_path.toml | 9 - cli/testdata/config/bad_entropy_group.toml | 8 - cli/testdata/config/base.toml | 10 - cli/testdata/config/entropy_group.toml | 8 - .../config/escaped_character_group.toml | 8 - cli/testdata/config/extend_1.toml | 10 - cli/testdata/config/extend_2.toml | 10 - cli/testdata/config/extend_3.toml | 9 - cli/testdata/config/generic.toml | 8 - cli/testdata/config/generic_with_py_path.toml | 36 - cli/testdata/config/path_only.toml | 6 - cli/testdata/config/simple.toml | 222 -- .../expected/git/small-branch-foo.txt | 17 - cli/testdata/expected/git/small.txt | 67 - cli/testdata/expected/report/csv_simple.csv | 2 - cli/testdata/expected/report/empty.json | 1 - cli/testdata/expected/report/json_simple.json | 22 - .../expected/report/sarif_simple.got.sarif | 302 -- .../expected/report/sarif_simple.sarif | 302 -- cli/testdata/repos/nogit/main.go | 24 - cli/testdata/repos/small/README.md | 2 - cli/testdata/repos/small/api/api.go | 7 - .../repos/small/dotGit/COMMIT_EDITMSG | 1 - cli/testdata/repos/small/dotGit/FETCH_HEAD | 1 - cli/testdata/repos/small/dotGit/HEAD | 1 - cli/testdata/repos/small/dotGit/ORIG_HEAD | 1 - cli/testdata/repos/small/dotGit/config | 13 - cli/testdata/repos/small/dotGit/description | 1 - cli/testdata/repos/small/dotGit/index | Bin 317 -> 0 bytes cli/testdata/repos/small/dotGit/info/exclude | 6 - cli/testdata/repos/small/dotGit/logs/HEAD | 13 - .../small/dotGit/logs/refs/heads/api-pkg | 1 - .../repos/small/dotGit/logs/refs/heads/foo | 3 - .../repos/small/dotGit/logs/refs/heads/main | 2 - .../dotGit/logs/refs/heads/remove-secrets | 3 - .../dotGit/logs/refs/remotes/origin/HEAD | 1 - .../dotGit/logs/refs/remotes/origin/api-pkg | 1 - .../small/dotGit/logs/refs/remotes/origin/foo | 1 - .../dotGit/logs/refs/remotes/origin/main | 1 - .../02/d85657604c34e7b7fbb324a0c6c8b13c2c3760 | 1 - .../15/2888a42422b2ff5868b8d003d626120a9cb738 | Bin 86 -> 0 bytes .../2e/1db472eeba53f06c4026ae4566ea022e36598e | Bin 618 -> 0 bytes .../49/1504d5a31946ce75e22554cc34203d8e5ff3ca | Bin 175 -> 0 bytes .../5c/547e4215d9594c3935bdfefdf4f500016a4112 | Bin 51 -> 0 bytes .../78/9ba677976d5db481de55c799d67acbf8e3f16a | Bin 51 -> 0 bytes .../90/6335481df9a4b48906c90318b4fac76b67fe73 | 3 - .../9a/932e37eaa9fb64b09e47e5e859c9b2c8cb47ad | Bin 196 -> 0 bytes .../a1/22b33c6bad3ee54724f52f2caad385ab1982ab | Bin 163 -> 0 bytes .../a5/caae6d742e49a33982f1fdc608ce861ea59be5 | Bin 134 -> 0 bytes .../a9/aa0c942dcef669a94f207a77426106b25efd1a | Bin 143 -> 0 bytes .../bc/f47ef84f29bb7ed6e653d61fccd30d0ecce886 | Bin 116 -> 0 bytes .../d8/32479114dc6be7207edc7c37ce91dd11b93161 | Bin 80 -> 0 bytes .../da/2622b4d97e32c5801511244b809144b6b3ea78 | Bin 51 -> 0 bytes .../e5/c0849a65c586eab87dcfc31fec74f0fd7c62cb | Bin 143 -> 0 bytes .../f1/b58b97808f8e744f6a23c693859df5b5968901 | Bin 176 -> 0 bytes ...dc2976b84768d0829c75cc8d8fc4d849be62cd.idx | Bin 1324 -> 0 bytes ...c2976b84768d0829c75cc8d8fc4d849be62cd.pack | Bin 2116 -> 0 bytes cli/testdata/repos/small/dotGit/packed-refs | 2 - .../repos/small/dotGit/refs/heads/api-pkg | 1 - .../repos/small/dotGit/refs/heads/foo | 1 - .../repos/small/dotGit/refs/heads/main | 1 - .../small/dotGit/refs/heads/remove-secrets | 1 - .../small/dotGit/refs/remotes/origin/HEAD | 1 - .../small/dotGit/refs/remotes/origin/api-pkg | 1 - .../small/dotGit/refs/remotes/origin/foo | 1 - .../small/dotGit/refs/remotes/origin/main | 1 - cli/testdata/repos/small/main.go | 27 - cli/testdata/repos/staged/.gitleaksignore | 1 - cli/testdata/repos/staged/README.md | 2 - cli/testdata/repos/staged/api/api.go | 10 - .../repos/staged/dotGit/COMMIT_EDITMSG | 1 - cli/testdata/repos/staged/dotGit/FETCH_HEAD | 1 - cli/testdata/repos/staged/dotGit/HEAD | 1 - cli/testdata/repos/staged/dotGit/ORIG_HEAD | 1 - cli/testdata/repos/staged/dotGit/config | 13 - cli/testdata/repos/staged/dotGit/description | 1 - cli/testdata/repos/staged/dotGit/index | Bin 359 -> 0 bytes cli/testdata/repos/staged/dotGit/info/exclude | 6 - cli/testdata/repos/staged/dotGit/logs/HEAD | 14 - .../staged/dotGit/logs/refs/heads/api-pkg | 1 - .../repos/staged/dotGit/logs/refs/heads/foo | 3 - .../repos/staged/dotGit/logs/refs/heads/main | 3 - .../dotGit/logs/refs/heads/remove-secrets | 3 - .../dotGit/logs/refs/remotes/origin/HEAD | 1 - .../dotGit/logs/refs/remotes/origin/api-pkg | 1 - .../dotGit/logs/refs/remotes/origin/foo | 1 - .../dotGit/logs/refs/remotes/origin/main | 1 - .../02/d85657604c34e7b7fbb324a0c6c8b13c2c3760 | 1 - .../15/2888a42422b2ff5868b8d003d626120a9cb738 | Bin 86 -> 0 bytes .../2e/1db472eeba53f06c4026ae4566ea022e36598e | Bin 618 -> 0 bytes .../46/18d7e4512b6b0b1dab85cf846d9f43474ec8be | Bin 44 -> 0 bytes .../49/1504d5a31946ce75e22554cc34203d8e5ff3ca | Bin 175 -> 0 bytes .../5c/547e4215d9594c3935bdfefdf4f500016a4112 | Bin 51 -> 0 bytes .../65/83d6db4a57bbeda62d50fc91649036d499418d | Bin 116 -> 0 bytes .../66/bc70d0c0bfbb6468b3f90c3f1e9f2ddba02b43 | Bin 155 -> 0 bytes .../78/9ba677976d5db481de55c799d67acbf8e3f16a | Bin 51 -> 0 bytes .../90/6335481df9a4b48906c90318b4fac76b67fe73 | 3 - .../9a/932e37eaa9fb64b09e47e5e859c9b2c8cb47ad | Bin 196 -> 0 bytes .../a1/22b33c6bad3ee54724f52f2caad385ab1982ab | Bin 163 -> 0 bytes .../a5/caae6d742e49a33982f1fdc608ce861ea59be5 | Bin 134 -> 0 bytes .../a9/aa0c942dcef669a94f207a77426106b25efd1a | Bin 143 -> 0 bytes .../b1/6d768dd595a59f947abe087901183d219d7e54 | Bin 182 -> 0 bytes .../bc/f47ef84f29bb7ed6e653d61fccd30d0ecce886 | Bin 116 -> 0 bytes .../bf/3f24164d7256b4021575cbdb2f97b98e6f057e | 2 - .../d8/32479114dc6be7207edc7c37ce91dd11b93161 | Bin 80 -> 0 bytes .../da/2622b4d97e32c5801511244b809144b6b3ea78 | Bin 51 -> 0 bytes .../e5/c0849a65c586eab87dcfc31fec74f0fd7c62cb | Bin 143 -> 0 bytes .../f1/b58b97808f8e744f6a23c693859df5b5968901 | Bin 176 -> 0 bytes ...dc2976b84768d0829c75cc8d8fc4d849be62cd.idx | Bin 1324 -> 0 bytes ...c2976b84768d0829c75cc8d8fc4d849be62cd.pack | Bin 2116 -> 0 bytes cli/testdata/repos/staged/dotGit/packed-refs | 2 - .../repos/staged/dotGit/refs/heads/api-pkg | 1 - .../repos/staged/dotGit/refs/heads/foo | 1 - .../repos/staged/dotGit/refs/heads/main | 1 - .../staged/dotGit/refs/heads/remove-secrets | 1 - .../staged/dotGit/refs/remotes/origin/HEAD | 1 - .../staged/dotGit/refs/remotes/origin/api-pkg | 1 - .../staged/dotGit/refs/remotes/origin/foo | 1 - .../staged/dotGit/refs/remotes/origin/main | 1 - cli/testdata/repos/staged/main.go | 27 - .../file_symlink/symlinked_id_ed25519 | 1 - .../repos/symlinks/source_file/id_ed25519 | 7 - cli/testdata/tmp/note.txt | 1 - cli/upload_to_cloudsmith.sh | 21 - 269 files changed, 25965 deletions(-) delete mode 100644 cli/.gitignore delete mode 100644 cli/.infisicalignore delete mode 100644 cli/agent-config.yaml delete mode 100644 cli/detect/baseline.go delete mode 100644 cli/detect/cmd/scm/scm.go delete mode 100644 cli/detect/config/allowlist.go delete mode 100644 cli/detect/config/config.go delete mode 100644 cli/detect/config/gitleaks.toml delete mode 100644 cli/detect/config/rule.go delete mode 100644 cli/detect/config/utils.go delete mode 100644 cli/detect/decoder.go delete mode 100644 cli/detect/detect.go delete mode 100644 cli/detect/directory.go delete mode 100644 cli/detect/git.go delete mode 100644 cli/detect/location.go delete mode 100644 cli/detect/logging/log.go delete mode 100644 cli/detect/reader.go delete mode 100644 cli/detect/regexp/stdlib_regex.go delete mode 100644 cli/detect/regexp/wasilibs_regex.go delete mode 100644 cli/detect/report/constants.go delete mode 100644 cli/detect/report/csv.go delete mode 100644 cli/detect/report/finding.go delete mode 100644 cli/detect/report/json.go delete mode 100644 cli/detect/report/junit.go delete mode 100644 cli/detect/report/report.go delete mode 100644 cli/detect/report/sarif.go delete mode 100644 cli/detect/report/template.go delete mode 100644 cli/detect/sources/directory.go delete mode 100644 cli/detect/sources/git.go delete mode 100644 cli/detect/utils.go delete mode 100644 cli/docker/alpine delete mode 100644 cli/go.mod delete mode 100644 cli/go.sum delete mode 100644 cli/goreleaser.dockerfile delete mode 100644 cli/infisical-cli.repo delete mode 100644 cli/main.go delete mode 100644 cli/packages/api/api.go delete mode 100644 cli/packages/api/errors.go delete mode 100644 cli/packages/api/model.go delete mode 100644 cli/packages/cmd/agent.go delete mode 100644 cli/packages/cmd/bootstrap.go delete mode 100644 cli/packages/cmd/cmd_test.go delete mode 100644 cli/packages/cmd/dynamic_secrets.go delete mode 100644 cli/packages/cmd/export.go delete mode 100644 cli/packages/cmd/export_test.go delete mode 100644 cli/packages/cmd/folder.go delete mode 100644 cli/packages/cmd/gateway.go delete mode 100644 cli/packages/cmd/init.go delete mode 100644 cli/packages/cmd/kmip.go delete mode 100644 cli/packages/cmd/login.go delete mode 100644 cli/packages/cmd/man.go delete mode 100644 cli/packages/cmd/pre-commit-script/pre-commit-without-bang.sh delete mode 100644 cli/packages/cmd/pre-commit-script/pre-commit.sh delete mode 100644 cli/packages/cmd/reset.go delete mode 100644 cli/packages/cmd/root.go delete mode 100644 cli/packages/cmd/run.go delete mode 100644 cli/packages/cmd/scan.go delete mode 100644 cli/packages/cmd/secrets.go delete mode 100644 cli/packages/cmd/ssh.go delete mode 100644 cli/packages/cmd/token.go delete mode 100644 cli/packages/cmd/tokens.go delete mode 100644 cli/packages/cmd/user.go delete mode 100644 cli/packages/cmd/vault.go delete mode 100644 cli/packages/config/config.go delete mode 100644 cli/packages/crypto/crypto.go delete mode 100644 cli/packages/gateway/connection.go delete mode 100644 cli/packages/gateway/constants.go delete mode 100644 cli/packages/gateway/gateway.go delete mode 100644 cli/packages/gateway/relay.go delete mode 100644 cli/packages/gateway/relay_windows.go delete mode 100644 cli/packages/gateway/systemd.go delete mode 100644 cli/packages/gateway/udp_listener/listener_unix.go delete mode 100644 cli/packages/gateway/udp_listener/listener_windows.go delete mode 100644 cli/packages/models/cli.go delete mode 100644 cli/packages/srp/client.go delete mode 100644 cli/packages/srp/params.go delete mode 100644 cli/packages/srp/server.go delete mode 100644 cli/packages/srp/srp.go delete mode 100644 cli/packages/srp/util.go delete mode 100644 cli/packages/systemd/daemon.go delete mode 100644 cli/packages/telemetry/telemetry.go delete mode 100644 cli/packages/util/agent.go delete mode 100644 cli/packages/util/auth.go delete mode 100644 cli/packages/util/check-for-update.go delete mode 100644 cli/packages/util/common.go delete mode 100644 cli/packages/util/config.go delete mode 100644 cli/packages/util/constants.go delete mode 100644 cli/packages/util/credentials.go delete mode 100644 cli/packages/util/exec.go delete mode 100644 cli/packages/util/folders.go delete mode 100644 cli/packages/util/helper.go delete mode 100644 cli/packages/util/init.go delete mode 100644 cli/packages/util/keyringwrapper.go delete mode 100644 cli/packages/util/log.go delete mode 100644 cli/packages/util/secrets.go delete mode 100644 cli/packages/util/testdata/infisical-branch-env.json delete mode 100644 cli/packages/util/testdata/infisical-default-env.json delete mode 100644 cli/packages/util/testdata/infisical-no-matching-branch-env.json delete mode 100644 cli/packages/util/vault.go delete mode 100644 cli/packages/visualize/dynamic_secret_leases.go delete mode 100644 cli/packages/visualize/folders.go delete mode 100644 cli/packages/visualize/secrets.go delete mode 100644 cli/packages/visualize/visualize.go delete mode 100755 cli/scripts/completions.sh delete mode 100644 cli/scripts/export_test_env.sh delete mode 100755 cli/scripts/install.sh delete mode 100755 cli/scripts/manpages.sh delete mode 100644 cli/scripts/setup.deb.sh delete mode 100644 cli/secret-render-template delete mode 100644 cli/test/.snapshots/test-TestServiceToken_ExportSecretsWithImports delete mode 100644 cli/test/.snapshots/test-TestServiceToken_ExportSecretsWithoutImports delete mode 100644 cli/test/.snapshots/test-TestServiceToken_GetSecretsByNameRecursive delete mode 100644 cli/test/.snapshots/test-TestServiceToken_GetSecretsByNameWithImports delete mode 100644 cli/test/.snapshots/test-TestServiceToken_GetSecretsByNameWithNotFoundSecret delete mode 100644 cli/test/.snapshots/test-TestServiceToken_RunCmdRecursiveAndImports delete mode 100644 cli/test/.snapshots/test-TestServiceToken_RunCmdWithImports delete mode 100644 cli/test/.snapshots/test-TestServiceToken_RunCmdWithoutImports delete mode 100644 cli/test/.snapshots/test-TestServiceToken_SecretsGetWithImportsAndRecursiveCmd delete mode 100644 cli/test/.snapshots/test-TestServiceToken_SecretsGetWithoutImportsAndWithoutRecursiveCmd delete mode 100644 cli/test/.snapshots/test-TestUniversalAuth_ExportSecretsWithImports delete mode 100644 cli/test/.snapshots/test-TestUniversalAuth_ExportSecretsWithoutImports delete mode 100644 cli/test/.snapshots/test-TestUniversalAuth_GetSecretsByNameRecursive delete mode 100644 cli/test/.snapshots/test-TestUniversalAuth_GetSecretsByNameWithImports delete mode 100644 cli/test/.snapshots/test-TestUniversalAuth_GetSecretsByNameWithNotFoundSecret delete mode 100644 cli/test/.snapshots/test-TestUniversalAuth_RunCmdRecursiveAndImports delete mode 100644 cli/test/.snapshots/test-TestUniversalAuth_RunCmdWithImports delete mode 100644 cli/test/.snapshots/test-TestUniversalAuth_RunCmdWithoutImports delete mode 100644 cli/test/.snapshots/test-TestUniversalAuth_SecretsGetWithImportsAndRecursiveCmd delete mode 100644 cli/test/.snapshots/test-TestUniversalAuth_SecretsGetWithoutImportsAndWithoutRecursiveCmd delete mode 100644 cli/test/.snapshots/test-TestUniversalAuth_SecretsGetWrongEnvironment delete mode 100644 cli/test/.snapshots/test-TestUserAuth_SecretsGetAll delete mode 100644 cli/test/.snapshots/test-testUserAuth_SecretsGetAllWithoutConnection delete mode 100644 cli/test/export_test.go delete mode 100644 cli/test/helper.go delete mode 100644 cli/test/login_test.go delete mode 100644 cli/test/main_test.go delete mode 100644 cli/test/run_test.go delete mode 100644 cli/test/secrets_by_name_test.go delete mode 100644 cli/test/secrets_test.go delete mode 100644 cli/testdata/baseline/baseline.csv delete mode 100644 cli/testdata/baseline/baseline.json delete mode 100644 cli/testdata/baseline/baseline.sarif delete mode 100644 cli/testdata/config/allow_aws_re.toml delete mode 100644 cli/testdata/config/allow_commit.toml delete mode 100644 cli/testdata/config/allow_global_aws_re.toml delete mode 100644 cli/testdata/config/allow_path.toml delete mode 100755 cli/testdata/config/bad_entropy_group.toml delete mode 100644 cli/testdata/config/base.toml delete mode 100755 cli/testdata/config/entropy_group.toml delete mode 100644 cli/testdata/config/escaped_character_group.toml delete mode 100644 cli/testdata/config/extend_1.toml delete mode 100644 cli/testdata/config/extend_2.toml delete mode 100644 cli/testdata/config/extend_3.toml delete mode 100644 cli/testdata/config/generic.toml delete mode 100644 cli/testdata/config/generic_with_py_path.toml delete mode 100644 cli/testdata/config/path_only.toml delete mode 100644 cli/testdata/config/simple.toml delete mode 100644 cli/testdata/expected/git/small-branch-foo.txt delete mode 100644 cli/testdata/expected/git/small.txt delete mode 100644 cli/testdata/expected/report/csv_simple.csv delete mode 100644 cli/testdata/expected/report/empty.json delete mode 100644 cli/testdata/expected/report/json_simple.json delete mode 100644 cli/testdata/expected/report/sarif_simple.got.sarif delete mode 100644 cli/testdata/expected/report/sarif_simple.sarif delete mode 100644 cli/testdata/repos/nogit/main.go delete mode 100644 cli/testdata/repos/small/README.md delete mode 100644 cli/testdata/repos/small/api/api.go delete mode 100644 cli/testdata/repos/small/dotGit/COMMIT_EDITMSG delete mode 100644 cli/testdata/repos/small/dotGit/FETCH_HEAD delete mode 100644 cli/testdata/repos/small/dotGit/HEAD delete mode 100644 cli/testdata/repos/small/dotGit/ORIG_HEAD delete mode 100644 cli/testdata/repos/small/dotGit/config delete mode 100644 cli/testdata/repos/small/dotGit/description delete mode 100644 cli/testdata/repos/small/dotGit/index delete mode 100644 cli/testdata/repos/small/dotGit/info/exclude delete mode 100644 cli/testdata/repos/small/dotGit/logs/HEAD delete mode 100644 cli/testdata/repos/small/dotGit/logs/refs/heads/api-pkg delete mode 100644 cli/testdata/repos/small/dotGit/logs/refs/heads/foo delete mode 100644 cli/testdata/repos/small/dotGit/logs/refs/heads/main delete mode 100644 cli/testdata/repos/small/dotGit/logs/refs/heads/remove-secrets delete mode 100644 cli/testdata/repos/small/dotGit/logs/refs/remotes/origin/HEAD delete mode 100644 cli/testdata/repos/small/dotGit/logs/refs/remotes/origin/api-pkg delete mode 100644 cli/testdata/repos/small/dotGit/logs/refs/remotes/origin/foo delete mode 100644 cli/testdata/repos/small/dotGit/logs/refs/remotes/origin/main delete mode 100644 cli/testdata/repos/small/dotGit/objects/02/d85657604c34e7b7fbb324a0c6c8b13c2c3760 delete mode 100644 cli/testdata/repos/small/dotGit/objects/15/2888a42422b2ff5868b8d003d626120a9cb738 delete mode 100644 cli/testdata/repos/small/dotGit/objects/2e/1db472eeba53f06c4026ae4566ea022e36598e delete mode 100644 cli/testdata/repos/small/dotGit/objects/49/1504d5a31946ce75e22554cc34203d8e5ff3ca delete mode 100644 cli/testdata/repos/small/dotGit/objects/5c/547e4215d9594c3935bdfefdf4f500016a4112 delete mode 100644 cli/testdata/repos/small/dotGit/objects/78/9ba677976d5db481de55c799d67acbf8e3f16a delete mode 100644 cli/testdata/repos/small/dotGit/objects/90/6335481df9a4b48906c90318b4fac76b67fe73 delete mode 100644 cli/testdata/repos/small/dotGit/objects/9a/932e37eaa9fb64b09e47e5e859c9b2c8cb47ad delete mode 100644 cli/testdata/repos/small/dotGit/objects/a1/22b33c6bad3ee54724f52f2caad385ab1982ab delete mode 100644 cli/testdata/repos/small/dotGit/objects/a5/caae6d742e49a33982f1fdc608ce861ea59be5 delete mode 100644 cli/testdata/repos/small/dotGit/objects/a9/aa0c942dcef669a94f207a77426106b25efd1a delete mode 100644 cli/testdata/repos/small/dotGit/objects/bc/f47ef84f29bb7ed6e653d61fccd30d0ecce886 delete mode 100644 cli/testdata/repos/small/dotGit/objects/d8/32479114dc6be7207edc7c37ce91dd11b93161 delete mode 100644 cli/testdata/repos/small/dotGit/objects/da/2622b4d97e32c5801511244b809144b6b3ea78 delete mode 100644 cli/testdata/repos/small/dotGit/objects/e5/c0849a65c586eab87dcfc31fec74f0fd7c62cb delete mode 100644 cli/testdata/repos/small/dotGit/objects/f1/b58b97808f8e744f6a23c693859df5b5968901 delete mode 100644 cli/testdata/repos/small/dotGit/objects/pack/pack-2cdc2976b84768d0829c75cc8d8fc4d849be62cd.idx delete mode 100644 cli/testdata/repos/small/dotGit/objects/pack/pack-2cdc2976b84768d0829c75cc8d8fc4d849be62cd.pack delete mode 100644 cli/testdata/repos/small/dotGit/packed-refs delete mode 100644 cli/testdata/repos/small/dotGit/refs/heads/api-pkg delete mode 100644 cli/testdata/repos/small/dotGit/refs/heads/foo delete mode 100644 cli/testdata/repos/small/dotGit/refs/heads/main delete mode 100644 cli/testdata/repos/small/dotGit/refs/heads/remove-secrets delete mode 100644 cli/testdata/repos/small/dotGit/refs/remotes/origin/HEAD delete mode 100644 cli/testdata/repos/small/dotGit/refs/remotes/origin/api-pkg delete mode 100644 cli/testdata/repos/small/dotGit/refs/remotes/origin/foo delete mode 100644 cli/testdata/repos/small/dotGit/refs/remotes/origin/main delete mode 100644 cli/testdata/repos/small/main.go delete mode 100644 cli/testdata/repos/staged/.gitleaksignore delete mode 100644 cli/testdata/repos/staged/README.md delete mode 100644 cli/testdata/repos/staged/api/api.go delete mode 100644 cli/testdata/repos/staged/dotGit/COMMIT_EDITMSG delete mode 100644 cli/testdata/repos/staged/dotGit/FETCH_HEAD delete mode 100644 cli/testdata/repos/staged/dotGit/HEAD delete mode 100644 cli/testdata/repos/staged/dotGit/ORIG_HEAD delete mode 100644 cli/testdata/repos/staged/dotGit/config delete mode 100644 cli/testdata/repos/staged/dotGit/description delete mode 100644 cli/testdata/repos/staged/dotGit/index delete mode 100644 cli/testdata/repos/staged/dotGit/info/exclude delete mode 100644 cli/testdata/repos/staged/dotGit/logs/HEAD delete mode 100644 cli/testdata/repos/staged/dotGit/logs/refs/heads/api-pkg delete mode 100644 cli/testdata/repos/staged/dotGit/logs/refs/heads/foo delete mode 100644 cli/testdata/repos/staged/dotGit/logs/refs/heads/main delete mode 100644 cli/testdata/repos/staged/dotGit/logs/refs/heads/remove-secrets delete mode 100644 cli/testdata/repos/staged/dotGit/logs/refs/remotes/origin/HEAD delete mode 100644 cli/testdata/repos/staged/dotGit/logs/refs/remotes/origin/api-pkg delete mode 100644 cli/testdata/repos/staged/dotGit/logs/refs/remotes/origin/foo delete mode 100644 cli/testdata/repos/staged/dotGit/logs/refs/remotes/origin/main delete mode 100644 cli/testdata/repos/staged/dotGit/objects/02/d85657604c34e7b7fbb324a0c6c8b13c2c3760 delete mode 100644 cli/testdata/repos/staged/dotGit/objects/15/2888a42422b2ff5868b8d003d626120a9cb738 delete mode 100644 cli/testdata/repos/staged/dotGit/objects/2e/1db472eeba53f06c4026ae4566ea022e36598e delete mode 100644 cli/testdata/repos/staged/dotGit/objects/46/18d7e4512b6b0b1dab85cf846d9f43474ec8be delete mode 100644 cli/testdata/repos/staged/dotGit/objects/49/1504d5a31946ce75e22554cc34203d8e5ff3ca delete mode 100644 cli/testdata/repos/staged/dotGit/objects/5c/547e4215d9594c3935bdfefdf4f500016a4112 delete mode 100644 cli/testdata/repos/staged/dotGit/objects/65/83d6db4a57bbeda62d50fc91649036d499418d delete mode 100644 cli/testdata/repos/staged/dotGit/objects/66/bc70d0c0bfbb6468b3f90c3f1e9f2ddba02b43 delete mode 100644 cli/testdata/repos/staged/dotGit/objects/78/9ba677976d5db481de55c799d67acbf8e3f16a delete mode 100644 cli/testdata/repos/staged/dotGit/objects/90/6335481df9a4b48906c90318b4fac76b67fe73 delete mode 100644 cli/testdata/repos/staged/dotGit/objects/9a/932e37eaa9fb64b09e47e5e859c9b2c8cb47ad delete mode 100644 cli/testdata/repos/staged/dotGit/objects/a1/22b33c6bad3ee54724f52f2caad385ab1982ab delete mode 100644 cli/testdata/repos/staged/dotGit/objects/a5/caae6d742e49a33982f1fdc608ce861ea59be5 delete mode 100644 cli/testdata/repos/staged/dotGit/objects/a9/aa0c942dcef669a94f207a77426106b25efd1a delete mode 100644 cli/testdata/repos/staged/dotGit/objects/b1/6d768dd595a59f947abe087901183d219d7e54 delete mode 100644 cli/testdata/repos/staged/dotGit/objects/bc/f47ef84f29bb7ed6e653d61fccd30d0ecce886 delete mode 100644 cli/testdata/repos/staged/dotGit/objects/bf/3f24164d7256b4021575cbdb2f97b98e6f057e delete mode 100644 cli/testdata/repos/staged/dotGit/objects/d8/32479114dc6be7207edc7c37ce91dd11b93161 delete mode 100644 cli/testdata/repos/staged/dotGit/objects/da/2622b4d97e32c5801511244b809144b6b3ea78 delete mode 100644 cli/testdata/repos/staged/dotGit/objects/e5/c0849a65c586eab87dcfc31fec74f0fd7c62cb delete mode 100644 cli/testdata/repos/staged/dotGit/objects/f1/b58b97808f8e744f6a23c693859df5b5968901 delete mode 100644 cli/testdata/repos/staged/dotGit/objects/pack/pack-2cdc2976b84768d0829c75cc8d8fc4d849be62cd.idx delete mode 100644 cli/testdata/repos/staged/dotGit/objects/pack/pack-2cdc2976b84768d0829c75cc8d8fc4d849be62cd.pack delete mode 100644 cli/testdata/repos/staged/dotGit/packed-refs delete mode 100644 cli/testdata/repos/staged/dotGit/refs/heads/api-pkg delete mode 100644 cli/testdata/repos/staged/dotGit/refs/heads/foo delete mode 100644 cli/testdata/repos/staged/dotGit/refs/heads/main delete mode 100644 cli/testdata/repos/staged/dotGit/refs/heads/remove-secrets delete mode 100644 cli/testdata/repos/staged/dotGit/refs/remotes/origin/HEAD delete mode 100644 cli/testdata/repos/staged/dotGit/refs/remotes/origin/api-pkg delete mode 100644 cli/testdata/repos/staged/dotGit/refs/remotes/origin/foo delete mode 100644 cli/testdata/repos/staged/dotGit/refs/remotes/origin/main delete mode 100644 cli/testdata/repos/staged/main.go delete mode 120000 cli/testdata/repos/symlinks/file_symlink/symlinked_id_ed25519 delete mode 100644 cli/testdata/repos/symlinks/source_file/id_ed25519 delete mode 100644 cli/testdata/tmp/note.txt delete mode 100755 cli/upload_to_cloudsmith.sh diff --git a/cli/.gitignore b/cli/.gitignore deleted file mode 100644 index 8eb54d72b..000000000 --- a/cli/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -.infisical.json -dist/ -agent-config.test.yaml -.test.env \ No newline at end of file diff --git a/cli/.infisicalignore b/cli/.infisicalignore deleted file mode 100644 index e5dfe29bc..000000000 --- a/cli/.infisicalignore +++ /dev/null @@ -1,3 +0,0 @@ -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 diff --git a/cli/agent-config.yaml b/cli/agent-config.yaml deleted file mode 100644 index 210c21413..000000000 --- a/cli/agent-config.yaml +++ /dev/null @@ -1,37 +0,0 @@ -infisical: - address: "https://app.infisical.com/" -auth: - type: "universal-auth" - config: - client-id: "./client-id" - client-secret: "./client-secret" - remove_client_secret_on_read: false -sinks: - - type: "file" - config: - path: "access-token" -templates: - - template-content: | - {{- with secret "202f04d7-e4cb-43d4-a292-e893712d61fc" "dev" "/" }} - {{- range . }} - {{ .Key }}={{ .Value }} - {{- end }} - {{- end }} - destination-path: my-dot-env-0.env - config: - polling-interval: 60s - execute: - command: docker-compose -f docker-compose.prod.yml down && docker-compose -f docker-compose.prod.yml up -d - - - base64-template-content: e3stIHdpdGggc2VjcmV0ICIyMDJmMDRkNy1lNGNiLTQzZDQtYTI5Mi1lODkzNzEyZDYxZmMiICJkZXYiICIvIiB9fQp7ey0gcmFuZ2UgLiB9fQp7eyAuS2V5IH19PXt7IC5WYWx1ZSB9fQp7ey0gZW5kIH19Cnt7LSBlbmQgfX0= - destination-path: my-dot-env.env - config: - polling-interval: 60s - execute: - command: docker-compose -f docker-compose.prod.yml down && docker-compose -f docker-compose.prod.yml up -d - - - source-path: my-dot-ev-secret-template1 - destination-path: my-dot-env-1.env - config: - exec: - command: mkdir hello-world1 diff --git a/cli/detect/baseline.go b/cli/detect/baseline.go deleted file mode 100644 index eeaa2a73a..000000000 --- a/cli/detect/baseline.go +++ /dev/null @@ -1,103 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package detect - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - - "github.com/Infisical/infisical-merge/detect/report" -) - -func IsNew(finding report.Finding, redact uint, baseline []report.Finding) bool { - // Explicitly testing each property as it gives significantly better performance in comparison to cmp.Equal(). Drawback is that - // the code requires maintenance if/when the Finding struct changes - for _, b := range baseline { - if finding.RuleID == b.RuleID && - finding.Description == b.Description && - finding.StartLine == b.StartLine && - finding.EndLine == b.EndLine && - finding.StartColumn == b.StartColumn && - finding.EndColumn == b.EndColumn && - (redact > 0 || (finding.Match == b.Match && finding.Secret == b.Secret)) && - finding.File == b.File && - finding.Commit == b.Commit && - finding.Author == b.Author && - finding.Email == b.Email && - finding.Date == b.Date && - finding.Message == b.Message && - // Omit checking finding.Fingerprint - if the format of the fingerprint changes, the users will see unexpected behaviour - finding.Entropy == b.Entropy { - return false - } - } - return true -} - -func LoadBaseline(baselinePath string) ([]report.Finding, error) { - bytes, err := os.ReadFile(baselinePath) - if err != nil { - return nil, fmt.Errorf("could not open %s", baselinePath) - } - - var previousFindings []report.Finding - err = json.Unmarshal(bytes, &previousFindings) - if err != nil { - return nil, fmt.Errorf("the format of the file %s is not supported", baselinePath) - } - - return previousFindings, nil -} - -func (d *Detector) AddBaseline(baselinePath string, source string) error { - if baselinePath != "" { - absoluteSource, err := filepath.Abs(source) - if err != nil { - return err - } - - absoluteBaseline, err := filepath.Abs(baselinePath) - if err != nil { - return err - } - - relativeBaseline, err := filepath.Rel(absoluteSource, absoluteBaseline) - if err != nil { - return err - } - - baseline, err := LoadBaseline(baselinePath) - if err != nil { - return err - } - - d.baseline = baseline - baselinePath = relativeBaseline - - } - - d.baselinePath = baselinePath - return nil -} diff --git a/cli/detect/cmd/scm/scm.go b/cli/detect/cmd/scm/scm.go deleted file mode 100644 index dddeffdf5..000000000 --- a/cli/detect/cmd/scm/scm.go +++ /dev/null @@ -1,70 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package scm - -import ( - "fmt" - "strings" -) - -type Platform int - -const ( - UnknownPlatform Platform = iota - NoPlatform // Explicitly disable the feature - GitHubPlatform - GitLabPlatform - AzureDevOpsPlatform - BitBucketPlatform - // TODO: Add others. -) - -func (p Platform) String() string { - return [...]string{ - "unknown", - "none", - "github", - "gitlab", - "azuredevops", - "bitbucket", - }[p] -} - -func PlatformFromString(s string) (Platform, error) { - switch strings.ToLower(s) { - case "", "unknown": - return UnknownPlatform, nil - case "none": - return NoPlatform, nil - case "github": - return GitHubPlatform, nil - case "gitlab": - return GitLabPlatform, nil - case "azuredevops": - return AzureDevOpsPlatform, nil - case "bitbucket": - return BitBucketPlatform, nil - default: - return UnknownPlatform, fmt.Errorf("invalid scm platform value: %s", s) - } -} diff --git a/cli/detect/config/allowlist.go b/cli/detect/config/allowlist.go deleted file mode 100644 index d91188f68..000000000 --- a/cli/detect/config/allowlist.go +++ /dev/null @@ -1,159 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package config - -import ( - "fmt" - "strings" - - "golang.org/x/exp/maps" - - "github.com/Infisical/infisical-merge/detect/regexp" -) - -type AllowlistMatchCondition int - -const ( - AllowlistMatchOr AllowlistMatchCondition = iota - AllowlistMatchAnd -) - -func (a AllowlistMatchCondition) String() string { - return [...]string{ - "OR", - "AND", - }[a] -} - -// Allowlist allows a rule to be ignored for specific -// regexes, paths, and/or commits -type Allowlist struct { - // Short human readable description of the allowlist. - Description string - - // MatchCondition determines whether all criteria must match. - MatchCondition AllowlistMatchCondition - - // Commits is a slice of commit SHAs that are allowed to be ignored. Defaults to "OR". - Commits []string - - // Paths is a slice of path regular expressions that are allowed to be ignored. - Paths []*regexp.Regexp - - // Can be `match` or `line`. - // - // If `match` the _Regexes_ will be tested against the match of the _Rule.Regex_. - // - // If `line` the _Regexes_ will be tested against the entire line. - // - // If RegexTarget is empty, it will be tested against the found secret. - RegexTarget string - - // Regexes is slice of content regular expressions that are allowed to be ignored. - Regexes []*regexp.Regexp - - // StopWords is a slice of stop words that are allowed to be ignored. - // This targets the _secret_, not the content of the regex match like the - // Regexes slice. - StopWords []string - - // validated is an internal flag to track whether `Validate()` has been called. - validated bool -} - -func (a *Allowlist) Validate() error { - if a.validated { - return nil - } - - // Disallow empty allowlists. - if len(a.Commits) == 0 && - len(a.Paths) == 0 && - len(a.Regexes) == 0 && - len(a.StopWords) == 0 { - return fmt.Errorf("must contain at least one check for: commits, paths, regexes, or stopwords") - } - - // Deduplicate commits and stopwords. - if len(a.Commits) > 0 { - uniqueCommits := make(map[string]struct{}) - for _, commit := range a.Commits { - uniqueCommits[commit] = struct{}{} - } - a.Commits = maps.Keys(uniqueCommits) - } - if len(a.StopWords) > 0 { - uniqueStopwords := make(map[string]struct{}) - for _, stopWord := range a.StopWords { - uniqueStopwords[stopWord] = struct{}{} - } - a.StopWords = maps.Keys(uniqueStopwords) - } - - a.validated = true - return nil -} - -// CommitAllowed returns true if the commit is allowed to be ignored. -func (a *Allowlist) CommitAllowed(c string) (bool, string) { - if a == nil || c == "" { - return false, "" - } - - for _, commit := range a.Commits { - if commit == c { - return true, c - } - } - return false, "" -} - -// PathAllowed returns true if the path is allowed to be ignored. -func (a *Allowlist) PathAllowed(path string) bool { - if a == nil || path == "" { - return false - } - return anyRegexMatch(path, a.Paths) -} - -// RegexAllowed returns true if the regex is allowed to be ignored. -func (a *Allowlist) RegexAllowed(secret string) bool { - if a == nil || secret == "" { - return false - } - return anyRegexMatch(secret, a.Regexes) -} - -func (a *Allowlist) ContainsStopWord(s string) (bool, string) { - if a == nil || s == "" { - return false, "" - } - - s = strings.ToLower(s) - for _, stopWord := range a.StopWords { - if strings.Contains(s, strings.ToLower(stopWord)) { - return true, stopWord - } - } - return false, "" -} diff --git a/cli/detect/config/config.go b/cli/detect/config/config.go deleted file mode 100644 index 10c6db7e0..000000000 --- a/cli/detect/config/config.go +++ /dev/null @@ -1,426 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package config - -import ( - _ "embed" - "errors" - "fmt" - "sort" - "strings" - - "github.com/spf13/viper" - - "github.com/Infisical/infisical-merge/detect/logging" - "github.com/Infisical/infisical-merge/detect/regexp" -) - -const DefaultScanConfigFileName = ".infisical-scan.toml" -const DefaultScanConfigEnvName = "INFISICAL_SCAN_CONFIG" -const DefaultInfisicalIgnoreFineName = ".infisicalignore" - -var ( - //go:embed gitleaks.toml - DefaultConfig string - - // use to keep track of how many configs we can extend - // yea I know, globals bad - extendDepth int -) - -const maxExtendDepth = 2 - -// ViperConfig is the config struct used by the Viper config package -// to parse the config file. This struct does not include regular expressions. -// It is used as an intermediary to convert the Viper config to the Config struct. -type ViperConfig struct { - Title string - Description string - Extend Extend - Rules []struct { - ID string - Description string - Path string - Regex string - SecretGroup int - Entropy float64 - Keywords []string - Tags []string - - // Deprecated: this is a shim for backwards-compatibility. - // TODO: Remove this in 9.x. - AllowList *viperRuleAllowlist - Allowlists []*viperRuleAllowlist - } - // Deprecated: this is a shim for backwards-compatibility. - // TODO: Remove this in 9.x. - AllowList *viperGlobalAllowlist - Allowlists []*viperGlobalAllowlist -} - -type viperRuleAllowlist struct { - Description string - Condition string - Commits []string - Paths []string - RegexTarget string - Regexes []string - StopWords []string -} - -type viperGlobalAllowlist struct { - TargetRules []string - viperRuleAllowlist `mapstructure:",squash"` -} - -// Config is a configuration struct that contains rules and an allowlist if present. -type Config struct { - Title string - Extend Extend - Path string - Description string - Rules map[string]Rule - Keywords map[string]struct{} - // used to keep sarif results consistent - OrderedRules []string - Allowlists []*Allowlist -} - -// Extend is a struct that allows users to define how they want their -// configuration extended by other configuration files. -type Extend struct { - Path string - URL string - UseDefault bool - DisabledRules []string -} - -func (vc *ViperConfig) Translate() (Config, error) { - var ( - keywords = make(map[string]struct{}) - orderedRules []string - rulesMap = make(map[string]Rule) - ruleAllowlists = make(map[string][]*Allowlist) - ) - - // Validate individual rules. - for _, vr := range vc.Rules { - var ( - pathPat *regexp.Regexp - regexPat *regexp.Regexp - ) - if vr.Path != "" { - pathPat = regexp.MustCompile(vr.Path) - } - if vr.Regex != "" { - regexPat = regexp.MustCompile(vr.Regex) - } - if vr.Keywords == nil { - vr.Keywords = []string{} - } else { - for i, k := range vr.Keywords { - keyword := strings.ToLower(k) - keywords[keyword] = struct{}{} - vr.Keywords[i] = keyword - } - } - if vr.Tags == nil { - vr.Tags = []string{} - } - cr := Rule{ - RuleID: vr.ID, - Description: vr.Description, - Regex: regexPat, - SecretGroup: vr.SecretGroup, - Entropy: vr.Entropy, - Path: pathPat, - Keywords: vr.Keywords, - Tags: vr.Tags, - } - - // Parse the rule allowlists, including the older format for backwards compatibility. - if vr.AllowList != nil { - // TODO: Remove this in v9. - if len(vr.Allowlists) > 0 { - return Config{}, fmt.Errorf("%s: [rules.allowlist] is deprecated, it cannot be used alongside [[rules.allowlist]]", cr.RuleID) - } - vr.Allowlists = append(vr.Allowlists, vr.AllowList) - } - for _, a := range vr.Allowlists { - allowlist, err := parseAllowlist(a) - if err != nil { - return Config{}, fmt.Errorf("%s: [[rules.allowlists]] %w", cr.RuleID, err) - } - cr.Allowlists = append(cr.Allowlists, allowlist) - } - orderedRules = append(orderedRules, cr.RuleID) - rulesMap[cr.RuleID] = cr - } - - // Assemble the config. - c := Config{ - Title: vc.Title, - Description: vc.Description, - Extend: vc.Extend, - Rules: rulesMap, - Keywords: keywords, - OrderedRules: orderedRules, - } - // Parse the config allowlists, including the older format for backwards compatibility. - if vc.AllowList != nil { - // TODO: Remove this in v9. - if len(vc.Allowlists) > 0 { - return Config{}, errors.New("[allowlist] is deprecated, it cannot be used alongside [[allowlists]]") - } - vc.Allowlists = append(vc.Allowlists, vc.AllowList) - } - for _, a := range vc.Allowlists { - allowlist, err := parseAllowlist(&a.viperRuleAllowlist) - if err != nil { - return Config{}, fmt.Errorf("[[allowlists]] %w", err) - } - // Allowlists with |targetRules| aren't added to the global list. - if len(a.TargetRules) > 0 { - for _, ruleID := range a.TargetRules { - // It's not possible to validate |ruleID| until after extend. - ruleAllowlists[ruleID] = append(ruleAllowlists[ruleID], allowlist) - } - } else { - c.Allowlists = append(c.Allowlists, allowlist) - } - } - - if maxExtendDepth != extendDepth { - // disallow both usedefault and path from being set - if c.Extend.Path != "" && c.Extend.UseDefault { - return Config{}, errors.New("unable to load config due to extend.path and extend.useDefault being set") - } - if c.Extend.UseDefault { - if err := c.extendDefault(); err != nil { - return Config{}, err - } - } else if c.Extend.Path != "" { - if err := c.extendPath(); err != nil { - return Config{}, err - } - } - } - - // Validate the rules after everything has been assembled (including extended configs). - if extendDepth == 0 { - for _, rule := range c.Rules { - if err := rule.Validate(); err != nil { - return Config{}, err - } - } - - // Populate targeted configs. - for ruleID, allowlists := range ruleAllowlists { - rule, ok := c.Rules[ruleID] - if !ok { - return Config{}, fmt.Errorf("[[allowlists]] target rule ID '%s' does not exist", ruleID) - } - rule.Allowlists = append(rule.Allowlists, allowlists...) - c.Rules[ruleID] = rule - } - } - - return c, nil -} - -func parseAllowlist(a *viperRuleAllowlist) (*Allowlist, error) { - var matchCondition AllowlistMatchCondition - switch strings.ToUpper(a.Condition) { - case "AND", "&&": - matchCondition = AllowlistMatchAnd - case "", "OR", "||": - matchCondition = AllowlistMatchOr - default: - return nil, fmt.Errorf("unknown allowlist |condition| '%s' (expected 'and', 'or')", a.Condition) - } - - // Validate the target. - regexTarget := a.RegexTarget - if regexTarget != "" { - switch regexTarget { - case "secret": - regexTarget = "" - case "match", "line": - // do nothing - default: - return nil, fmt.Errorf("unknown allowlist |regexTarget| '%s' (expected 'match', 'line')", regexTarget) - } - } - var allowlistRegexes []*regexp.Regexp - for _, a := range a.Regexes { - allowlistRegexes = append(allowlistRegexes, regexp.MustCompile(a)) - } - var allowlistPaths []*regexp.Regexp - for _, a := range a.Paths { - allowlistPaths = append(allowlistPaths, regexp.MustCompile(a)) - } - - allowlist := &Allowlist{ - Description: a.Description, - MatchCondition: matchCondition, - Commits: a.Commits, - Paths: allowlistPaths, - RegexTarget: regexTarget, - Regexes: allowlistRegexes, - StopWords: a.StopWords, - } - if err := allowlist.Validate(); err != nil { - return nil, err - } - return allowlist, nil -} - -func (c *Config) GetOrderedRules() []Rule { - var orderedRules []Rule - for _, id := range c.OrderedRules { - if _, ok := c.Rules[id]; ok { - orderedRules = append(orderedRules, c.Rules[id]) - } - } - return orderedRules -} - -func (c *Config) extendDefault() error { - extendDepth++ - viper.SetConfigType("toml") - if err := viper.ReadConfig(strings.NewReader(DefaultConfig)); err != nil { - return fmt.Errorf("failed to load extended default config, err: %w", err) - } - defaultViperConfig := ViperConfig{} - if err := viper.Unmarshal(&defaultViperConfig); err != nil { - return fmt.Errorf("failed to load extended default config, err: %w", err) - } - cfg, err := defaultViperConfig.Translate() - if err != nil { - return fmt.Errorf("failed to load extended default config, err: %w", err) - - } - logging.Debug().Msg("extending config with default config") - c.extend(cfg) - return nil -} - -func (c *Config) extendPath() error { - extendDepth++ - viper.SetConfigFile(c.Extend.Path) - if err := viper.ReadInConfig(); err != nil { - return fmt.Errorf("failed to load extended config, err: %w", err) - } - extensionViperConfig := ViperConfig{} - if err := viper.Unmarshal(&extensionViperConfig); err != nil { - return fmt.Errorf("failed to load extended config, err: %w", err) - } - cfg, err := extensionViperConfig.Translate() - if err != nil { - return fmt.Errorf("failed to load extended config, err: %w", err) - } - logging.Debug().Msgf("extending config with %s", c.Extend.Path) - c.extend(cfg) - return nil -} - -func (c *Config) extendURL() { - // TODO -} - -func (c *Config) extend(extensionConfig Config) { - // Get config name for helpful log messages. - var configName string - if c.Extend.Path != "" { - configName = c.Extend.Path - } else { - configName = "default" - } - // Convert |Config.DisabledRules| into a map for ease of access. - disabledRuleIDs := map[string]struct{}{} - for _, id := range c.Extend.DisabledRules { - if _, ok := extensionConfig.Rules[id]; !ok { - logging.Warn(). - Str("rule-id", id). - Str("config", configName). - Msg("Disabled rule doesn't exist in extended config.") - } - disabledRuleIDs[id] = struct{}{} - } - - for ruleID, baseRule := range extensionConfig.Rules { - // Skip the rule. - if _, ok := disabledRuleIDs[ruleID]; ok { - logging.Debug(). - Str("rule-id", ruleID). - Str("config", configName). - Msg("Ignoring rule from extended config.") - continue - } - - currentRule, ok := c.Rules[ruleID] - if !ok { - // Rule doesn't exist, add it to the config. - c.Rules[ruleID] = baseRule - for _, k := range baseRule.Keywords { - c.Keywords[k] = struct{}{} - } - c.OrderedRules = append(c.OrderedRules, ruleID) - } else { - // Rule exists, merge our changes into the base. - if currentRule.Description != "" { - baseRule.Description = currentRule.Description - } - if currentRule.Entropy != 0 { - baseRule.Entropy = currentRule.Entropy - } - if currentRule.SecretGroup != 0 { - baseRule.SecretGroup = currentRule.SecretGroup - } - if currentRule.Regex != nil { - baseRule.Regex = currentRule.Regex - } - if currentRule.Path != nil { - baseRule.Path = currentRule.Path - } - baseRule.Tags = append(baseRule.Tags, currentRule.Tags...) - baseRule.Keywords = append(baseRule.Keywords, currentRule.Keywords...) - for _, a := range currentRule.Allowlists { - baseRule.Allowlists = append(baseRule.Allowlists, a) - } - // The keywords from the base rule and the extended rule must be merged into the global keywords list - for _, k := range baseRule.Keywords { - c.Keywords[k] = struct{}{} - } - c.Rules[ruleID] = baseRule - } - } - - // append allowlists, not attempting to merge - for _, a := range extensionConfig.Allowlists { - c.Allowlists = append(c.Allowlists, a) - } - - // sort to keep extended rules in order - sort.Strings(c.OrderedRules) -} diff --git a/cli/detect/config/gitleaks.toml b/cli/detect/config/gitleaks.toml deleted file mode 100644 index 92a06a319..000000000 --- a/cli/detect/config/gitleaks.toml +++ /dev/null @@ -1,3130 +0,0 @@ -# This file has been auto-generated. Do not edit manually. -# If you would like to contribute new rules, please use -# cmd/generate/config/main.go and follow the contributing guidelines -# at https://github.com/gitleaks/gitleaks/blob/master/CONTRIBUTING.md -# -# How the hell does secret scanning work? Read this: -# https://lookingatcomputer.substack.com/p/regex-is-almost-all-you-need -# -# This is the default gitleaks configuration file. -# Rules and allowlists are defined within this file. -# Rules instruct gitleaks on what should be considered a secret. -# Allowlists instruct gitleaks on what is allowed, i.e. not a secret. - -title = "gitleaks config" - -# TODO: change to [[allowlists]] -[allowlist] -description = "global allow lists" -paths = [ - '''gitleaks\.toml''', - '''(?i)\.(?:bmp|gif|jpe?g|png|svg|tiff?)$''', - '''(?i)\.(?:eot|[ot]tf|woff2?)$''', - '''(?i)\.(?:docx?|xlsx?|pdf|bin|socket|vsidx|v2|suo|wsuo|.dll|pdb|exe|gltf|zip)$''', - '''go\.(?:mod|sum|work(?:\.sum)?)$''', - '''(?:^|/)vendor/modules\.txt$''', - '''(?:^|/)vendor/(?:github\.com|golang\.org/x|google\.golang\.org|gopkg\.in|istio\.io|k8s\.io|sigs\.k8s\.io)(?:/.*)?$''', - '''(?:^|/)gradlew(?:\.bat)?$''', - '''(?:^|/)gradle\.lockfile$''', - '''(?:^|/)mvnw(?:\.cmd)?$''', - '''(?:^|/)\.mvn/wrapper/MavenWrapperDownloader\.java$''', - '''(?:^|/)node_modules(?:/.*)?$''', - '''(?:^|/)(?:deno\.lock|npm-shrinkwrap\.json|package-lock\.json|pnpm-lock\.yaml|yarn\.lock)$''', - '''(?:^|/)bower_components(?:/.*)?$''', - '''(?:^|/)(?:angular|bootstrap|jquery(?:-?ui)?|plotly|swagger-?ui)[a-zA-Z0-9.-]*(?:\.min)?\.js(?:\.map)?$''', - '''(?:^|/)javascript\.json$''', - '''(?:^|/)(?:Pipfile|poetry)\.lock$''', - '''(?i)(?:^|/)(?:v?env|virtualenv)/lib(?:64)?(?:/.*)?$''', - '''(?i)(?:^|/)(?:lib(?:64)?/python[23](?:\.\d{1,2})+|python/[23](?:\.\d{1,2})+/lib(?:64)?)(?:/.*)?$''', - '''(?i)(?:^|/)[a-z0-9_.]+-[0-9.]+\.dist-info(?:/.+)?$''', - '''(?:^|/)vendor/(?:bundle|ruby)(?:/.*?)?$''', - '''\.gem$''', - '''verification-metadata\.xml''', - '''Database.refactorlog''', -] -regexes = [ - '''(?i)^true|false|null$''', - '''^(?i:a+|b+|c+|d+|e+|f+|g+|h+|i+|j+|k+|l+|m+|n+|o+|p+|q+|r+|s+|t+|u+|v+|w+|x+|y+|z+|\*+|\.+)$''', - '''^\$(?:\d+|{\d+})$''', - '''^\$(?:[A-Z_]+|[a-z_]+)$''', - '''^\${(?:[A-Z_]+|[a-z_]+)}$''', - '''^\{\{[ \t]*[\w ().|]+[ \t]*}}$''', - '''^\$\{\{[ \t]*(?:(?:env|github|secrets|vars)(?:\.[A-Za-z]\w+)+[\w "'&./=|]*)[ \t]*}}$''', - '''^%(?:[A-Z_]+|[a-z_]+)%$''', - '''^%[+\-# 0]?[bcdeEfFgGoOpqstTUvxX]$''', - '''^\{\d{0,2}}$''', - '''^@(?:[A-Z_]+|[a-z_]+)@$''', - '''^/Users/(?i)[a-z0-9]+/[\w .-/]+$''', - '''^/(?:bin|etc|home|opt|tmp|usr|var)/[\w ./-]+$''', -] -stopwords = [ - "abcdefghijklmnopqrstuvwxyz", - "014df517-39d1-4453-b7b3-9930c563627c", -] - -[[rules]] -id = "1password-secret-key" -description = "Uncovered a possible 1Password secret key, potentially compromising access to secrets in vaults." -regex = '''\bA3-[A-Z0-9]{6}-(?:(?:[A-Z0-9]{11})|(?:[A-Z0-9]{6}-[A-Z0-9]{5}))-[A-Z0-9]{5}-[A-Z0-9]{5}-[A-Z0-9]{5}\b''' -entropy = 3.8 -keywords = ["a3-"] - -[[rules]] -id = "1password-service-account-token" -description = "Uncovered a possible 1Password service account token, potentially compromising access to secrets in vaults." -regex = '''ops_eyJ[a-zA-Z0-9+/]{250,}={0,3}''' -entropy = 4 -keywords = ["ops_"] - -[[rules]] -id = "adafruit-api-key" -description = "Identified a potential Adafruit API Key, which could lead to unauthorized access to Adafruit services and sensitive data exposure." -regex = '''(?i)[\w.-]{0,50}?(?:adafruit)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9_-]{32})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["adafruit"] - -[[rules]] -id = "adobe-client-id" -description = "Detected a pattern that resembles an Adobe OAuth Web Client ID, posing a risk of compromised Adobe integrations and data breaches." -regex = '''(?i)[\w.-]{0,50}?(?:adobe)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-f0-9]{32})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = ["adobe"] - -[[rules]] -id = "adobe-client-secret" -description = "Discovered a potential Adobe Client Secret, which, if exposed, could allow unauthorized Adobe service access and data manipulation." -regex = '''\b(p8e-(?i)[a-z0-9]{32})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = ["p8e-"] - -[[rules]] -id = "age-secret-key" -description = "Discovered a potential Age encryption tool secret key, risking data decryption and unauthorized access to sensitive information." -regex = '''AGE-SECRET-KEY-1[QPZRY9X8GF2TVDW0S3JN54KHCE6MUA7L]{58}''' -keywords = ["age-secret-key-1"] - -[[rules]] -id = "airtable-api-key" -description = "Uncovered a possible Airtable API Key, potentially compromising database access and leading to data leakage or alteration." -regex = '''(?i)[\w.-]{0,50}?(?:airtable)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{17})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["airtable"] - -[[rules]] -id = "algolia-api-key" -description = "Identified an Algolia API Key, which could result in unauthorized search operations and data exposure on Algolia-managed platforms." -regex = '''(?i)[\w.-]{0,50}?(?:algolia)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{32})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["algolia"] - -[[rules]] -id = "alibaba-access-key-id" -description = "Detected an Alibaba Cloud AccessKey ID, posing a risk of unauthorized cloud resource access and potential data compromise." -regex = '''\b(LTAI(?i)[a-z0-9]{20})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = ["ltai"] - -[[rules]] -id = "alibaba-secret-key" -description = "Discovered a potential Alibaba Cloud Secret Key, potentially allowing unauthorized operations and data access within Alibaba Cloud." -regex = '''(?i)[\w.-]{0,50}?(?:alibaba)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{30})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = ["alibaba"] - -[[rules]] -id = "asana-client-id" -description = "Discovered a potential Asana Client ID, risking unauthorized access to Asana projects and sensitive task information." -regex = '''(?i)[\w.-]{0,50}?(?:asana)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([0-9]{16})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["asana"] - -[[rules]] -id = "asana-client-secret" -description = "Identified an Asana Client Secret, which could lead to compromised project management integrity and unauthorized access." -regex = '''(?i)[\w.-]{0,50}?(?:asana)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{32})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["asana"] - -[[rules]] -id = "atlassian-api-token" -description = "Detected an Atlassian API token, posing a threat to project management and collaboration tool security and data confidentiality." -regex = '''[\w.-]{0,50}?(?i:[\w.-]{0,50}?(?:atlassian|confluence|jira)(?:[ \t\w.-]{0,20})[\s'"]{0,3})(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-zA-Z0-9]{24})(?:[\x60'"\s;]|\\[nr]|$)|\b(ATATT3[A-Za-z0-9_\-=]{186})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3.5 -keywords = [ - "atlassian", - "confluence", - "jira", - "atatt3", -] - -[[rules]] -id = "authress-service-client-access-key" -description = "Uncovered a possible Authress Service Client Access Key, which may compromise access control services and sensitive data." -regex = '''\b((?:sc|ext|scauth|authress)_(?i)[a-z0-9]{5,30}\.[a-z0-9]{4,6}\.(?-i:acc)[_-][a-z0-9-]{10,32}\.[a-z0-9+/_=-]{30,120})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = [ - "sc_", - "ext_", - "scauth_", - "authress_", -] - -[[rules]] -id = "aws-access-token" -description = "Identified a pattern that may indicate AWS credentials, risking unauthorized cloud resource access and data breaches on AWS platforms." -regex = '''\b((?:A3T[A-Z0-9]|AKIA|ASIA|ABIA|ACCA)[A-Z0-9]{16})\b''' -entropy = 3 -keywords = [ - "a3t", - "akia", - "asia", - "abia", - "acca", -] -[[rules.allowlists]] -regexes = [ - '''.+EXAMPLE$''', -] - -[[rules]] -id = "azure-ad-client-secret" -description = "Azure AD Client Secret" -regex = '''(?:^|[\\'"\x60\s>=:(,)])([a-zA-Z0-9_~.]{3}\dQ~[a-zA-Z0-9_~.-]{31,34})(?:$|[\\'"\x60\s<),])''' -entropy = 3 -keywords = ["q~"] - -[[rules]] -id = "beamer-api-token" -description = "Detected a Beamer API token, potentially compromising content management and exposing sensitive notifications and updates." -regex = '''(?i)[\w.-]{0,50}?(?:beamer)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}(b_[a-z0-9=_\-]{44})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["beamer"] - -[[rules]] -id = "bitbucket-client-id" -description = "Discovered a potential Bitbucket Client ID, risking unauthorized repository access and potential codebase exposure." -regex = '''(?i)[\w.-]{0,50}?(?:bitbucket)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{32})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["bitbucket"] - -[[rules]] -id = "bitbucket-client-secret" -description = "Discovered a potential Bitbucket Client Secret, posing a risk of compromised code repositories and unauthorized access." -regex = '''(?i)[\w.-]{0,50}?(?:bitbucket)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9=_\-]{64})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["bitbucket"] - -[[rules]] -id = "bittrex-access-key" -description = "Identified a Bittrex Access Key, which could lead to unauthorized access to cryptocurrency trading accounts and financial loss." -regex = '''(?i)[\w.-]{0,50}?(?:bittrex)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{32})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["bittrex"] - -[[rules]] -id = "bittrex-secret-key" -description = "Detected a Bittrex Secret Key, potentially compromising cryptocurrency transactions and financial security." -regex = '''(?i)[\w.-]{0,50}?(?:bittrex)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{32})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["bittrex"] - -[[rules]] -id = "cisco-meraki-api-key" -description = "Cisco Meraki is a cloud-managed IT solution that provides networking, security, and device management through an easy-to-use interface." -regex = '''[\w.-]{0,50}?(?i:[\w.-]{0,50}?(?:(?-i:[Mm]eraki|MERAKI))(?:[ \t\w.-]{0,20})[\s'"]{0,3})(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([0-9a-f]{40})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = ["meraki"] - -[[rules]] -id = "clickhouse-cloud-api-secret-key" -description = "Identified a pattern that may indicate clickhouse cloud API secret key, risking unauthorized clickhouse cloud api access and data breaches on ClickHouse Cloud platforms." -regex = '''\b(4b1d[A-Za-z0-9]{38})\b''' -entropy = 3 -keywords = ["4b1d"] - -[[rules]] -id = "clojars-api-token" -description = "Uncovered a possible Clojars API token, risking unauthorized access to Clojure libraries and potential code manipulation." -regex = '''(?i)CLOJARS_[a-z0-9]{60}''' -entropy = 2 -keywords = ["clojars_"] - -[[rules]] -id = "cloudflare-api-key" -description = "Detected a Cloudflare API Key, potentially compromising cloud application deployments and operational security." -regex = '''(?i)[\w.-]{0,50}?(?:cloudflare)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9_-]{40})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = ["cloudflare"] - -[[rules]] -id = "cloudflare-global-api-key" -description = "Detected a Cloudflare Global API Key, potentially compromising cloud application deployments and operational security." -regex = '''(?i)[\w.-]{0,50}?(?:cloudflare)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-f0-9]{37})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = ["cloudflare"] - -[[rules]] -id = "cloudflare-origin-ca-key" -description = "Detected a Cloudflare Origin CA Key, potentially compromising cloud application deployments and operational security." -regex = '''\b(v1\.0-[a-f0-9]{24}-[a-f0-9]{146})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = [ - "cloudflare", - "v1.0-", -] - -[[rules]] -id = "codecov-access-token" -description = "Found a pattern resembling a Codecov Access Token, posing a risk of unauthorized access to code coverage reports and sensitive data." -regex = '''(?i)[\w.-]{0,50}?(?:codecov)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{32})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["codecov"] - -[[rules]] -id = "cohere-api-token" -description = "Identified a Cohere Token, posing a risk of unauthorized access to AI services and data manipulation." -regex = '''[\w.-]{0,50}?(?i:[\w.-]{0,50}?(?:cohere|CO_API_KEY)(?:[ \t\w.-]{0,20})[\s'"]{0,3})(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-zA-Z0-9]{40})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 4 -keywords = [ - "cohere", - "co_api_key", -] - -[[rules]] -id = "coinbase-access-token" -description = "Detected a Coinbase Access Token, posing a risk of unauthorized access to cryptocurrency accounts and financial transactions." -regex = '''(?i)[\w.-]{0,50}?(?:coinbase)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9_-]{64})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["coinbase"] - -[[rules]] -id = "confluent-access-token" -description = "Identified a Confluent Access Token, which could compromise access to streaming data platforms and sensitive data flow." -regex = '''(?i)[\w.-]{0,50}?(?:confluent)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{16})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["confluent"] - -[[rules]] -id = "confluent-secret-key" -description = "Found a Confluent Secret Key, potentially risking unauthorized operations and data access within Confluent services." -regex = '''(?i)[\w.-]{0,50}?(?:confluent)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{64})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["confluent"] - -[[rules]] -id = "contentful-delivery-api-token" -description = "Discovered a Contentful delivery API token, posing a risk to content management systems and data integrity." -regex = '''(?i)[\w.-]{0,50}?(?:contentful)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9=_\-]{43})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["contentful"] - -[[rules]] -id = "curl-auth-header" -description = "Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource." -regex = '''\bcurl\b(?:.*?|.*?(?:[\r\n]{1,2}.*?){1,5})[ \t\n\r](?:-H|--header)(?:=|[ \t]{0,5})(?:"(?i)(?:Authorization:[ \t]{0,5}(?:Basic[ \t]([a-z0-9+/]{8,}={0,3})|(?:Bearer|(?:Api-)?Token)[ \t]([\w=~@.+/-]{8,})|([\w=~@.+/-]{8,}))|(?:(?:X-(?:[a-z]+-)?)?(?:Api-?)?(?:Key|Token)):[ \t]{0,5}([\w=~@.+/-]{8,}))"|'(?i)(?:Authorization:[ \t]{0,5}(?:Basic[ \t]([a-z0-9+/]{8,}={0,3})|(?:Bearer|(?:Api-)?Token)[ \t]([\w=~@.+/-]{8,})|([\w=~@.+/-]{8,}))|(?:(?:X-(?:[a-z]+-)?)?(?:Api-?)?(?:Key|Token)):[ \t]{0,5}([\w=~@.+/-]{8,}))')(?:\B|\s|\z)''' -entropy = 2.75 -keywords = ["curl"] - -[[rules]] -id = "curl-auth-user" -description = "Discovered a potential basic authorization token provided in a curl command, which could compromise the curl accessed resource." -regex = '''\bcurl\b(?:.*|.*(?:[\r\n]{1,2}.*){1,5})[ \t\n\r](?:-u|--user)(?:=|[ \t]{0,5})("(:[^"]{3,}|[^:"]{3,}:|[^:"]{3,}:[^"]{3,})"|'([^:']{3,}:[^']{3,})'|((?:"[^"]{3,}"|'[^']{3,}'|[\w$@.-]+):(?:"[^"]{3,}"|'[^']{3,}'|[\w${}@.-]+)))(?:\s|\z)''' -entropy = 2 -keywords = ["curl"] -[[rules.allowlists]] -regexes = [ - '''[^:]+:(?:change(?:it|me)|pass(?:word)?|pwd|test|token|\*+|x+)''', - '''['"]?<[^>]+>['"]?:['"]?<[^>]+>|<[^:]+:[^>]+>['"]?''', - '''[^:]+:\[[^]]+]''', - '''['"]?[^:]+['"]?:['"]?\$(?:\d|\w+|\{(?:\d|\w+)})['"]?''', - '''\$\([^)]+\):\$\([^)]+\)''', - '''['"]?\$?{{[^}]+}}['"]?:['"]?\$?{{[^}]+}}['"]?''', -] - -[[rules]] -id = "databricks-api-token" -description = "Uncovered a Databricks API token, which may compromise big data analytics platforms and sensitive data processing." -regex = '''\b(dapi[a-f0-9]{32}(?:-\d)?)(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = ["dapi"] - -[[rules]] -id = "datadog-access-token" -description = "Detected a Datadog Access Token, potentially risking monitoring and analytics data exposure and manipulation." -regex = '''(?i)[\w.-]{0,50}?(?:datadog)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{40})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["datadog"] - -[[rules]] -id = "defined-networking-api-token" -description = "Identified a Defined Networking API token, which could lead to unauthorized network operations and data breaches." -regex = '''(?i)[\w.-]{0,50}?(?:dnkey)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}(dnkey-[a-z0-9=_\-]{26}-[a-z0-9=_\-]{52})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["dnkey"] - -[[rules]] -id = "digitalocean-access-token" -description = "Found a DigitalOcean OAuth Access Token, risking unauthorized cloud resource access and data compromise." -regex = '''\b(doo_v1_[a-f0-9]{64})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = ["doo_v1_"] - -[[rules]] -id = "digitalocean-pat" -description = "Discovered a DigitalOcean Personal Access Token, posing a threat to cloud infrastructure security and data privacy." -regex = '''\b(dop_v1_[a-f0-9]{64})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = ["dop_v1_"] - -[[rules]] -id = "digitalocean-refresh-token" -description = "Uncovered a DigitalOcean OAuth Refresh Token, which could allow prolonged unauthorized access and resource manipulation." -regex = '''(?i)\b(dor_v1_[a-f0-9]{64})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["dor_v1_"] - -[[rules]] -id = "discord-api-token" -description = "Detected a Discord API key, potentially compromising communication channels and user data privacy on Discord." -regex = '''(?i)[\w.-]{0,50}?(?:discord)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-f0-9]{64})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["discord"] - -[[rules]] -id = "discord-client-id" -description = "Identified a Discord client ID, which may lead to unauthorized integrations and data exposure in Discord applications." -regex = '''(?i)[\w.-]{0,50}?(?:discord)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([0-9]{18})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = ["discord"] - -[[rules]] -id = "discord-client-secret" -description = "Discovered a potential Discord client secret, risking compromised Discord bot integrations and data leaks." -regex = '''(?i)[\w.-]{0,50}?(?:discord)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9=_\-]{32})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = ["discord"] - -[[rules]] -id = "doppler-api-token" -description = "Discovered a Doppler API token, posing a risk to environment and secrets management security." -regex = '''dp\.pt\.(?i)[a-z0-9]{43}''' -entropy = 2 -keywords = ["dp.pt."] - -[[rules]] -id = "droneci-access-token" -description = "Detected a Droneci Access Token, potentially compromising continuous integration and deployment workflows." -regex = '''(?i)[\w.-]{0,50}?(?:droneci)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{32})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["droneci"] - -[[rules]] -id = "dropbox-api-token" -description = "Identified a Dropbox API secret, which could lead to unauthorized file access and data breaches in Dropbox storage." -regex = '''(?i)[\w.-]{0,50}?(?:dropbox)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{15})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["dropbox"] - -[[rules]] -id = "dropbox-long-lived-api-token" -description = "Found a Dropbox long-lived API token, risking prolonged unauthorized access to cloud storage and sensitive data." -regex = '''(?i)[\w.-]{0,50}?(?:dropbox)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{11}(AAAAAAAAAA)[a-z0-9\-_=]{43})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["dropbox"] - -[[rules]] -id = "dropbox-short-lived-api-token" -description = "Discovered a Dropbox short-lived API token, posing a risk of temporary but potentially harmful data access and manipulation." -regex = '''(?i)[\w.-]{0,50}?(?:dropbox)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}(sl\.[a-z0-9\-=_]{135})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["dropbox"] - -[[rules]] -id = "duffel-api-token" -description = "Uncovered a Duffel API token, which may compromise travel platform integrations and sensitive customer data." -regex = '''duffel_(?:test|live)_(?i)[a-z0-9_\-=]{43}''' -entropy = 2 -keywords = ["duffel_"] - -[[rules]] -id = "dynatrace-api-token" -description = "Detected a Dynatrace API token, potentially risking application performance monitoring and data exposure." -regex = '''dt0c01\.(?i)[a-z0-9]{24}\.[a-z0-9]{64}''' -entropy = 4 -keywords = ["dt0c01."] - -[[rules]] -id = "easypost-api-token" -description = "Identified an EasyPost API token, which could lead to unauthorized postal and shipment service access and data exposure." -regex = '''\bEZAK(?i)[a-z0-9]{54}\b''' -entropy = 2 -keywords = ["ezak"] - -[[rules]] -id = "easypost-test-api-token" -description = "Detected an EasyPost test API token, risking exposure of test environments and potentially sensitive shipment data." -regex = '''\bEZTK(?i)[a-z0-9]{54}\b''' -entropy = 2 -keywords = ["eztk"] - -[[rules]] -id = "etsy-access-token" -description = "Found an Etsy Access Token, potentially compromising Etsy shop management and customer data." -regex = '''(?i)[\w.-]{0,50}?(?:(?-i:ETSY|[Ee]tsy))(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{24})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = ["etsy"] - -[[rules]] -id = "facebook-access-token" -description = "Discovered a Facebook Access Token, posing a risk of unauthorized access to Facebook accounts and personal data exposure." -regex = '''(?i)\b(\d{15,16}(\||%)[0-9a-z\-_]{27,40})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = ["facebook"] - -[[rules]] -id = "facebook-page-access-token" -description = "Discovered a Facebook Page Access Token, posing a risk of unauthorized access to Facebook accounts and personal data exposure." -regex = '''\b(EAA[MC](?i)[a-z0-9]{100,})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 4 -keywords = [ - "eaam", - "eaac", -] - -[[rules]] -id = "facebook-secret" -description = "Discovered a Facebook Application secret, posing a risk of unauthorized access to Facebook accounts and personal data exposure." -regex = '''(?i)[\w.-]{0,50}?(?:facebook)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-f0-9]{32})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = ["facebook"] - -[[rules]] -id = "fastly-api-token" -description = "Uncovered a Fastly API key, which may compromise CDN and edge cloud services, leading to content delivery and security issues." -regex = '''(?i)[\w.-]{0,50}?(?:fastly)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9=_\-]{32})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["fastly"] - -[[rules]] -id = "finicity-api-token" -description = "Detected a Finicity API token, potentially risking financial data access and unauthorized financial operations." -regex = '''(?i)[\w.-]{0,50}?(?:finicity)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-f0-9]{32})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["finicity"] - -[[rules]] -id = "finicity-client-secret" -description = "Identified a Finicity Client Secret, which could lead to compromised financial service integrations and data breaches." -regex = '''(?i)[\w.-]{0,50}?(?:finicity)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{20})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["finicity"] - -[[rules]] -id = "finnhub-access-token" -description = "Found a Finnhub Access Token, risking unauthorized access to financial market data and analytics." -regex = '''(?i)[\w.-]{0,50}?(?:finnhub)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{20})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["finnhub"] - -[[rules]] -id = "flickr-access-token" -description = "Discovered a Flickr Access Token, posing a risk of unauthorized photo management and potential data leakage." -regex = '''(?i)[\w.-]{0,50}?(?:flickr)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{32})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["flickr"] - -[[rules]] -id = "flutterwave-encryption-key" -description = "Uncovered a Flutterwave Encryption Key, which may compromise payment processing and sensitive financial information." -regex = '''FLWSECK_TEST-(?i)[a-h0-9]{12}''' -entropy = 2 -keywords = ["flwseck_test"] - -[[rules]] -id = "flutterwave-public-key" -description = "Detected a Finicity Public Key, potentially exposing public cryptographic operations and integrations." -regex = '''FLWPUBK_TEST-(?i)[a-h0-9]{32}-X''' -entropy = 2 -keywords = ["flwpubk_test"] - -[[rules]] -id = "flutterwave-secret-key" -description = "Identified a Flutterwave Secret Key, risking unauthorized financial transactions and data breaches." -regex = '''FLWSECK_TEST-(?i)[a-h0-9]{32}-X''' -entropy = 2 -keywords = ["flwseck_test"] - -[[rules]] -id = "flyio-access-token" -description = "Uncovered a Fly.io API key" -regex = '''\b((?:fo1_[\w-]{43}|fm1[ar]_[a-zA-Z0-9+\/]{100,}={0,3}|fm2_[a-zA-Z0-9+\/]{100,}={0,3}))(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 4 -keywords = [ - "fo1_", - "fm1", - "fm2_", -] - -[[rules]] -id = "frameio-api-token" -description = "Found a Frame.io API token, potentially compromising video collaboration and project management." -regex = '''fio-u-(?i)[a-z0-9\-_=]{64}''' -keywords = ["fio-u-"] - -[[rules]] -id = "freemius-secret-key" -description = "Detected a Freemius secret key, potentially exposing sensitive information." -regex = '''(?i)["']secret_key["']\s*=>\s*["'](sk_[\S]{29})["']''' -path = '''(?i)\.php$''' -keywords = ["secret_key"] - -[[rules]] -id = "freshbooks-access-token" -description = "Discovered a Freshbooks Access Token, posing a risk to accounting software access and sensitive financial data exposure." -regex = '''(?i)[\w.-]{0,50}?(?:freshbooks)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{64})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["freshbooks"] - -[[rules]] -id = "gcp-api-key" -description = "Uncovered a GCP API key, which could lead to unauthorized access to Google Cloud services and data breaches." -regex = '''\b(AIza[\w-]{35})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 4 -keywords = ["aiza"] -[[rules.allowlists]] -regexes = [ - '''AIzaSyabcdefghijklmnopqrstuvwxyz1234567''', - '''AIzaSyAnLA7NfeLquW1tJFpx_eQCxoX-oo6YyIs''', - '''AIzaSyCkEhVjf3pduRDt6d1yKOMitrUEke8agEM''', - '''AIzaSyDMAScliyLx7F0NPDEJi1QmyCgHIAODrlU''', - '''AIzaSyD3asb-2pEZVqMkmL6M9N6nHZRR_znhrh0''', - '''AIzayDNSXIbFmlXbIE6mCzDLQAqITYefhixbX4A''', - '''AIzaSyAdOS2zB6NCsk1pCdZ4-P6GBdi_UUPwX7c''', - '''AIzaSyASWm6HmTMdYWpgMnjRBjxcQ9CKctWmLd4''', - '''AIzaSyANUvH9H9BsUccjsu2pCmEkOPjjaXeDQgY''', - '''AIzaSyA5_iVawFQ8ABuTZNUdcwERLJv_a_p4wtM''', - '''AIzaSyA4UrcGxgwQFTfaI3no3t7Lt1sjmdnP5sQ''', - '''AIzaSyDSb51JiIcB6OJpwwMicseKRhhrOq1cS7g''', - '''AIzaSyBF2RrAIm4a0mO64EShQfqfd2AFnzAvvuU''', - '''AIzaSyBcE-OOIbhjyR83gm4r2MFCu4MJmprNXsw''', - '''AIzaSyB8qGxt4ec15vitgn44duC5ucxaOi4FmqE''', - '''AIzaSyA8vmApnrHNFE0bApF4hoZ11srVL_n0nvY''', -] - -[[rules]] -id = "generic-api-key" -description = "Detected a Generic API Key, potentially exposing access to various services and sensitive operations." -regex = '''(?i)[\w.-]{0,50}?(?:access|auth|(?-i:[Aa]pi|API)|credential|creds|key|passw(?:or)?d|secret|token)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([\w.=-]{10,150}|[a-z0-9][a-z0-9+/]{11,}={0,3})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3.5 -keywords = [ - "access", - "api", - "auth", - "key", - "credential", - "creds", - "passwd", - "password", - "secret", - "token", -] -[[rules.allowlists]] -regexes = [ - '''^[a-zA-Z_.-]+$''', -] -[[rules.allowlists]] -description = "Allowlist for Generic API Keys" -regexTarget = "match" -regexes = [ - '''(?i)(?:access(?:ibility|or)|access[_.-]?id|random[_.-]?access|api[_.-]?(?:id|name|version)|rapid|capital|[a-z0-9-]*?api[a-z0-9-]*?:jar:|author|X-MS-Exchange-Organization-Auth|Authentication-Results|(?:credentials?[_.-]?id|withCredentials)|(?:bucket|foreign|hot|idx|natural|primary|pub(?:lic)?|schema|sequence)[_.-]?key|(?:turkey)|key[_.-]?(?:alias|board|code|frame|id|length|mesh|name|pair|press(?:ed)?|ring|selector|signature|size|stone|storetype|word|up|down|left|right)|key[_.-]?vault[_.-]?(?:id|name)|keyVaultToStoreSecrets|key(?:store|tab)[_.-]?(?:file|path)|issuerkeyhash|(?-i:[DdMm]onkey|[DM]ONKEY)|keying|(?:secret)[_.-]?(?:length|name|size)|UserSecretsId|(?:csrf)[_.-]?token|(?:io\.jsonwebtoken[ \t]?:[ \t]?[\w-]+)|(?:api|credentials|token)[_.-]?(?:endpoint|ur[il])|public[_.-]?token|(?:key|token)[_.-]?file|(?-i:(?:[A-Z_]+=\n[A-Z_]+=|[a-z_]+=\n[a-z_]+=)(?:\n|\z))|(?-i:(?:[A-Z.]+=\n[A-Z.]+=|[a-z.]+=\n[a-z.]+=)(?:\n|\z)))''', -] -stopwords = [ - "000000", - "6fe4476ee5a1832882e326b506d14126", - "_ec2_", - "aaaaaa", - "about", - "abstract", - "academy", - "acces", - "account", - "act-", - "act.", - "act_", - "action", - "active", - "actively", - "activity", - "adapter", - "add-", - "add-on", - "add.", - "add_", - "addon", - "addres", - "admin", - "adobe", - "advanced", - "adventure", - "agent", - "agile", - "air-", - "air.", - "air_", - "ajax", - "akka", - "alert", - "alfred", - "algorithm", - "all-", - "all.", - "all_", - "alloy", - "alpha", - "amazon", - "amqp", - "analysi", - "analytic", - "analyzer", - "android", - "angular", - "angularj", - "animate", - "animation", - "another", - "ansible", - "answer", - "ant-", - "ant.", - "ant_", - "any-", - "any.", - "any_", - "apache", - "app-", - "app.", - "app_", - "apple", - "arch", - "archive", - "archived", - "arduino", - "array", - "art-", - "art.", - "art_", - "article", - "asp-", - "asp.", - "asp_", - "asset", - "async", - "atom", - "attention", - "audio", - "audit", - "aura", - "auth", - "author", - "authorize", - "auto", - "automated", - "automatic", - "awesome", - "aws_", - "azure", - "back", - "backbone", - "backend", - "backup", - "bar-", - "bar.", - "bar_", - "base", - "based", - "bash", - "basic", - "batch", - "been", - "beer", - "behavior", - "being", - "benchmark", - "best", - "beta", - "better", - "big-", - "big.", - "big_", - "binary", - "binding", - "bit-", - "bit.", - "bit_", - "bitcoin", - "block", - "blog", - "board", - "book", - "bookmark", - "boost", - "boot", - "bootstrap", - "bosh", - "bot-", - "bot.", - "bot_", - "bower", - "box-", - "box.", - "box_", - "boxen", - "bracket", - "branch", - "bridge", - "browser", - "brunch", - "buffer", - "bug-", - "bug.", - "bug_", - "build", - "builder", - "building", - "buildout", - "buildpack", - "built", - "bundle", - "busines", - "but-", - "but.", - "but_", - "button", - "cache", - "caching", - "cakephp", - "calendar", - "call", - "camera", - "campfire", - "can-", - "can.", - "can_", - "canva", - "captcha", - "capture", - "card", - "carousel", - "case", - "cassandra", - "cat-", - "cat.", - "cat_", - "category", - "center", - "cento", - "challenge", - "change", - "changelog", - "channel", - "chart", - "chat", - "cheat", - "check", - "checker", - "chef", - "ches", - "chinese", - "chosen", - "chrome", - "ckeditor", - "clas", - "classe", - "classic", - "clean", - "cli-", - "cli.", - "cli_", - "client", - "clojure", - "clone", - "closure", - "cloud", - "club", - "cluster", - "cms-", - "cms_", - "coco", - "code", - "coding", - "coffee", - "color", - "combination", - "combo", - "command", - "commander", - "comment", - "commit", - "common", - "community", - "compas", - "compiler", - "complete", - "component", - "composer", - "computer", - "computing", - "con-", - "con.", - "con_", - "concept", - "conf", - "config", - "connect", - "connector", - "console", - "contact", - "container", - "contao", - "content", - "contest", - "context", - "control", - "convert", - "converter", - "conway'", - "cookbook", - "cookie", - "cool", - "copy", - "cordova", - "core", - "couchbase", - "couchdb", - "countdown", - "counter", - "course", - "craft", - "crawler", - "create", - "creating", - "creator", - "credential", - "crm-", - "crm.", - "crm_", - "cros", - "crud", - "csv-", - "csv.", - "csv_", - "cube", - "cucumber", - "cuda", - "current", - "currently", - "custom", - "daemon", - "dark", - "dart", - "dash", - "dashboard", - "data", - "database", - "date", - "day-", - "day.", - "day_", - "dead", - "debian", - "debug", - "debugger", - "deck", - "define", - "del-", - "del.", - "del_", - "delete", - "demo", - "deploy", - "design", - "designer", - "desktop", - "detection", - "detector", - "dev-", - "dev.", - "dev_", - "develop", - "developer", - "device", - "devise", - "diff", - "digital", - "directive", - "directory", - "discovery", - "display", - "django", - "dns-", - "dns_", - "doc-", - "doc.", - "doc_", - "docker", - "docpad", - "doctrine", - "document", - "doe-", - "doe.", - "doe_", - "dojo", - "dom-", - "dom.", - "dom_", - "domain", - "don't", - "done", - "dot-", - "dot.", - "dot_", - "dotfile", - "download", - "draft", - "drag", - "drill", - "drive", - "driven", - "driver", - "drop", - "dropbox", - "drupal", - "dsl-", - "dsl.", - "dsl_", - "dynamic", - "easy", - "ecdsa", - "eclipse", - "edit", - "editing", - "edition", - "editor", - "element", - "emac", - "email", - "embed", - "embedded", - "ember", - "emitter", - "emulator", - "encoding", - "endpoint", - "engine", - "english", - "enhanced", - "entity", - "entry", - "env_", - "episode", - "erlang", - "error", - "espresso", - "event", - "evented", - "example", - "exchange", - "exercise", - "experiment", - "expire", - "exploit", - "explorer", - "export", - "exporter", - "expres", - "ext-", - "ext.", - "ext_", - "extended", - "extension", - "external", - "extra", - "extractor", - "fabric", - "facebook", - "factory", - "fake", - "fast", - "feature", - "feed", - "fewfwef", - "ffmpeg", - "field", - "file", - "filter", - "find", - "finder", - "firefox", - "firmware", - "first", - "fish", - "fix-", - "fix_", - "flash", - "flask", - "flat", - "flex", - "flexible", - "flickr", - "flow", - "fluent", - "fluentd", - "fluid", - "folder", - "font", - "force", - "foreman", - "fork", - "form", - "format", - "formatter", - "forum", - "foundry", - "framework", - "free", - "friend", - "friendly", - "front-end", - "frontend", - "ftp-", - "ftp.", - "ftp_", - "fuel", - "full", - "fun-", - "fun.", - "fun_", - "func", - "future", - "gaia", - "gallery", - "game", - "gateway", - "gem-", - "gem.", - "gem_", - "gen-", - "gen.", - "gen_", - "general", - "generator", - "generic", - "genetic", - "get-", - "get.", - "get_", - "getenv", - "getting", - "ghost", - "gist", - "git-", - "git.", - "git_", - "github", - "gitignore", - "gitlab", - "glas", - "gmail", - "gnome", - "gnu-", - "gnu.", - "gnu_", - "goal", - "golang", - "gollum", - "good", - "google", - "gpu-", - "gpu.", - "gpu_", - "gradle", - "grail", - "graph", - "graphic", - "great", - "grid", - "groovy", - "group", - "grunt", - "guard", - "gui-", - "gui.", - "gui_", - "guide", - "guideline", - "gulp", - "gwt-", - "gwt.", - "gwt_", - "hack", - "hackathon", - "hacker", - "hacking", - "hadoop", - "haml", - "handler", - "hardware", - "has-", - "has_", - "hash", - "haskell", - "have", - "haxe", - "hello", - "help", - "helper", - "here", - "hero", - "heroku", - "high", - "hipchat", - "history", - "home", - "homebrew", - "homepage", - "hook", - "host", - "hosting", - "hot-", - "hot.", - "hot_", - "house", - "how-", - "how.", - "how_", - "html", - "http", - "hub-", - "hub.", - "hub_", - "hubot", - "human", - "icon", - "ide-", - "ide.", - "ide_", - "idea", - "identity", - "idiomatic", - "image", - "impact", - "import", - "important", - "importer", - "impres", - "index", - "infinite", - "info", - "injection", - "inline", - "input", - "inside", - "inspector", - "instagram", - "install", - "installer", - "instant", - "intellij", - "interface", - "internet", - "interview", - "into", - "intro", - "ionic", - "iphone", - "ipython", - "irc-", - "irc_", - "iso-", - "iso.", - "iso_", - "issue", - "jade", - "jasmine", - "java", - "jbos", - "jekyll", - "jenkin", - "jetbrains", - "job-", - "job.", - "job_", - "joomla", - "jpa-", - "jpa.", - "jpa_", - "jquery", - "json", - "just", - "kafka", - "karma", - "kata", - "kernel", - "keyboard", - "kindle", - "kit-", - "kit.", - "kit_", - "kitchen", - "knife", - "koan", - "kohana", - "lab-", - "lab.", - "lab_", - "lambda", - "lamp", - "language", - "laravel", - "last", - "latest", - "latex", - "launcher", - "layer", - "layout", - "lazy", - "ldap", - "leaflet", - "league", - "learn", - "learning", - "led-", - "led.", - "led_", - "leetcode", - "les-", - "les.", - "les_", - "level", - "leveldb", - "lib-", - "lib.", - "lib_", - "librarie", - "library", - "license", - "life", - "liferay", - "light", - "lightbox", - "like", - "line", - "link", - "linked", - "linkedin", - "linux", - "lisp", - "list", - "lite", - "little", - "load", - "loader", - "local", - "location", - "lock", - "log-", - "log.", - "log_", - "logger", - "logging", - "logic", - "login", - "logstash", - "longer", - "look", - "love", - "lua-", - "lua.", - "lua_", - "mac-", - "mac.", - "mac_", - "machine", - "made", - "magento", - "magic", - "mail", - "make", - "maker", - "making", - "man-", - "man.", - "man_", - "manage", - "manager", - "manifest", - "manual", - "map-", - "map.", - "map_", - "mapper", - "mapping", - "markdown", - "markup", - "master", - "math", - "matrix", - "maven", - "md5", - "mean", - "media", - "mediawiki", - "meetup", - "memcached", - "memory", - "menu", - "merchant", - "message", - "messaging", - "meta", - "metadata", - "meteor", - "method", - "metric", - "micro", - "middleman", - "migration", - "minecraft", - "miner", - "mini", - "minimal", - "mirror", - "mit-", - "mit.", - "mit_", - "mobile", - "mocha", - "mock", - "mod-", - "mod.", - "mod_", - "mode", - "model", - "modern", - "modular", - "module", - "modx", - "money", - "mongo", - "mongodb", - "mongoid", - "mongoose", - "monitor", - "monkey", - "more", - "motion", - "moved", - "movie", - "mozilla", - "mqtt", - "mule", - "multi", - "multiple", - "music", - "mustache", - "mvc-", - "mvc.", - "mvc_", - "mysql", - "nagio", - "name", - "native", - "need", - "neo-", - "neo.", - "neo_", - "nest", - "nested", - "net-", - "net.", - "net_", - "nette", - "network", - "new-", - "new.", - "new_", - "next", - "nginx", - "ninja", - "nlp-", - "nlp.", - "nlp_", - "node", - "nodej", - "nosql", - "not-", - "not.", - "not_", - "note", - "notebook", - "notepad", - "notice", - "notifier", - "now-", - "now.", - "now_", - "number", - "oauth", - "object", - "objective", - "obsolete", - "ocaml", - "octopres", - "official", - "old-", - "old.", - "old_", - "onboard", - "online", - "only", - "open", - "opencv", - "opengl", - "openshift", - "openwrt", - "option", - "oracle", - "org-", - "org.", - "org_", - "origin", - "original", - "orm-", - "orm.", - "orm_", - "osx-", - "osx_", - "our-", - "our.", - "our_", - "out-", - "out.", - "out_", - "output", - "over", - "overview", - "own-", - "own.", - "own_", - "pack", - "package", - "packet", - "page", - "panel", - "paper", - "paperclip", - "para", - "parallax", - "parallel", - "parse", - "parser", - "parsing", - "particle", - "party", - "password", - "patch", - "path", - "pattern", - "payment", - "paypal", - "pdf-", - "pdf.", - "pdf_", - "pebble", - "people", - "perl", - "personal", - "phalcon", - "phoenix", - "phone", - "phonegap", - "photo", - "php-", - "php.", - "php_", - "physic", - "picker", - "pipeline", - "platform", - "play", - "player", - "please", - "plu-", - "plu.", - "plu_", - "plug-in", - "plugin", - "plupload", - "png-", - "png.", - "png_", - "poker", - "polyfill", - "polymer", - "pool", - "pop-", - "pop.", - "pop_", - "popcorn", - "popup", - "port", - "portable", - "portal", - "portfolio", - "post", - "power", - "powered", - "powerful", - "prelude", - "pretty", - "preview", - "principle", - "print", - "pro-", - "pro.", - "pro_", - "problem", - "proc", - "product", - "profile", - "profiler", - "program", - "progres", - "project", - "protocol", - "prototype", - "provider", - "proxy", - "public", - "pull", - "puppet", - "pure", - "purpose", - "push", - "pusher", - "pyramid", - "python", - "quality", - "query", - "queue", - "quick", - "rabbitmq", - "rack", - "radio", - "rail", - "railscast", - "random", - "range", - "raspberry", - "rdf-", - "rdf.", - "rdf_", - "react", - "reactive", - "read", - "reader", - "readme", - "ready", - "real", - "real-time", - "reality", - "realtime", - "recipe", - "recorder", - "red-", - "red.", - "red_", - "reddit", - "redi", - "redmine", - "reference", - "refinery", - "refresh", - "registry", - "related", - "release", - "remote", - "rendering", - "repo", - "report", - "request", - "require", - "required", - "requirej", - "research", - "resource", - "response", - "resque", - "rest", - "restful", - "resume", - "reveal", - "reverse", - "review", - "riak", - "rich", - "right", - "ring", - "robot", - "role", - "room", - "router", - "routing", - "rpc-", - "rpc.", - "rpc_", - "rpg-", - "rpg.", - "rpg_", - "rspec", - "ruby-", - "ruby.", - "ruby_", - "rule", - "run-", - "run.", - "run_", - "runner", - "running", - "runtime", - "rust", - "rvm-", - "rvm.", - "rvm_", - "salt", - "sample", - "sandbox", - "sas-", - "sas.", - "sas_", - "sbt-", - "sbt.", - "sbt_", - "scala", - "scalable", - "scanner", - "schema", - "scheme", - "school", - "science", - "scraper", - "scratch", - "screen", - "script", - "scroll", - "scs-", - "scs.", - "scs_", - "sdk-", - "sdk.", - "sdk_", - "sdl-", - "sdl.", - "sdl_", - "search", - "secure", - "security", - "see-", - "see.", - "see_", - "seed", - "select", - "selector", - "selenium", - "semantic", - "sencha", - "send", - "sentiment", - "serie", - "server", - "service", - "session", - "set-", - "set.", - "set_", - "setting", - "setup", - "sha1", - "sha2", - "sha256", - "share", - "shared", - "sharing", - "sheet", - "shell", - "shield", - "shipping", - "shop", - "shopify", - "shortener", - "should", - "show", - "showcase", - "side", - "silex", - "simple", - "simulator", - "single", - "site", - "skeleton", - "sketch", - "skin", - "slack", - "slide", - "slider", - "slim", - "small", - "smart", - "smtp", - "snake", - "snapshot", - "snippet", - "soap", - "social", - "socket", - "software", - "solarized", - "solr", - "solution", - "solver", - "some", - "soon", - "source", - "space", - "spark", - "spatial", - "spec", - "sphinx", - "spine", - "spotify", - "spree", - "spring", - "sprite", - "sql-", - "sql.", - "sql_", - "sqlite", - "ssh-", - "ssh.", - "ssh_", - "stack", - "staging", - "standard", - "stanford", - "start", - "started", - "starter", - "startup", - "stat", - "statamic", - "state", - "static", - "statistic", - "statsd", - "statu", - "steam", - "step", - "still", - "stm-", - "stm.", - "stm_", - "storage", - "store", - "storm", - "story", - "strategy", - "stream", - "streaming", - "string", - "stripe", - "structure", - "studio", - "study", - "stuff", - "style", - "sublime", - "sugar", - "suite", - "summary", - "super", - "support", - "supported", - "svg-", - "svg.", - "svg_", - "svn-", - "svn.", - "svn_", - "swagger", - "swift", - "switch", - "switcher", - "symfony", - "symphony", - "sync", - "synopsi", - "syntax", - "system", - "tab-", - "tab.", - "tab_", - "table", - "tag-", - "tag.", - "tag_", - "talk", - "target", - "task", - "tcp-", - "tcp.", - "tcp_", - "tdd-", - "tdd.", - "tdd_", - "team", - "tech", - "template", - "term", - "terminal", - "testing", - "tetri", - "text", - "textmate", - "theme", - "theory", - "three", - "thrift", - "time", - "timeline", - "timer", - "tiny", - "tinymce", - "tip-", - "tip.", - "tip_", - "title", - "todo", - "todomvc", - "token", - "tool", - "toolbox", - "toolkit", - "top-", - "top.", - "top_", - "tornado", - "touch", - "tower", - "tracker", - "tracking", - "traffic", - "training", - "transfer", - "translate", - "transport", - "tree", - "trello", - "try-", - "try.", - "try_", - "tumblr", - "tut-", - "tut.", - "tut_", - "tutorial", - "tweet", - "twig", - "twitter", - "type", - "typo", - "ubuntu", - "uiview", - "ultimate", - "under", - "unit", - "unity", - "universal", - "unix", - "update", - "updated", - "upgrade", - "upload", - "uploader", - "uri-", - "uri.", - "uri_", - "url-", - "url.", - "url_", - "usage", - "usb-", - "usb.", - "usb_", - "use-", - "use.", - "use_", - "used", - "useful", - "user", - "using", - "util", - "utilitie", - "utility", - "vagrant", - "validator", - "value", - "variou", - "varnish", - "version", - "via-", - "via.", - "via_", - "video", - "view", - "viewer", - "vim-", - "vim.", - "vim_", - "vimrc", - "virtual", - "vision", - "visual", - "vpn", - "want", - "warning", - "watch", - "watcher", - "wave", - "way-", - "way.", - "way_", - "weather", - "web-", - "web_", - "webapp", - "webgl", - "webhook", - "webkit", - "webrtc", - "website", - "websocket", - "welcome", - "what", - "what'", - "when", - "where", - "which", - "why-", - "why.", - "why_", - "widget", - "wifi", - "wiki", - "win-", - "win.", - "win_", - "window", - "wip-", - "wip.", - "wip_", - "within", - "without", - "wizard", - "word", - "wordpres", - "work", - "worker", - "workflow", - "working", - "workshop", - "world", - "wrapper", - "write", - "writer", - "writing", - "written", - "www-", - "www.", - "www_", - "xamarin", - "xcode", - "xml-", - "xml.", - "xml_", - "xmpp", - "xxxxxx", - "yahoo", - "yaml", - "yandex", - "yeoman", - "yet-", - "yet.", - "yet_", - "yii-", - "yii.", - "yii_", - "youtube", - "yui-", - "yui.", - "yui_", - "zend", - "zero", - "zip-", - "zip.", - "zip_", - "zsh-", - "zsh.", - "zsh_", -] -[[rules.allowlists]] -regexTarget = "line" -regexes = [ - '''--mount=type=secret,''', - '''import[ \t]+{[ \t\w,]+}[ \t]+from[ \t]+['"][^'"]+['"]''', -] -[[rules.allowlists]] -condition = "AND" -paths = [ - '''\.bb$''','''\.bbappend$''','''\.bbclass$''','''\.inc$''', -] -regexTarget = "line" -regexes = [ - '''LICENSE[^=]*=\s*"[^"]+''', - '''LIC_FILES_CHKSUM[^=]*=\s*"[^"]+''', - '''SRC[^=]*=\s*"[a-zA-Z0-9]+''', -] - -[[rules]] -id = "github-app-token" -description = "Identified a GitHub App Token, which may compromise GitHub application integrations and source code security." -regex = '''(?:ghu|ghs)_[0-9a-zA-Z]{36}''' -entropy = 3 -keywords = [ - "ghu_", - "ghs_", -] -[[rules.allowlists]] -paths = [ - '''(?:^|/)@octokit/auth-token/README\.md$''', -] - -[[rules]] -id = "github-fine-grained-pat" -description = "Found a GitHub Fine-Grained Personal Access Token, risking unauthorized repository access and code manipulation." -regex = '''github_pat_\w{82}''' -entropy = 3 -keywords = ["github_pat_"] - -[[rules]] -id = "github-oauth" -description = "Discovered a GitHub OAuth Access Token, posing a risk of compromised GitHub account integrations and data leaks." -regex = '''gho_[0-9a-zA-Z]{36}''' -entropy = 3 -keywords = ["gho_"] - -[[rules]] -id = "github-pat" -description = "Uncovered a GitHub Personal Access Token, potentially leading to unauthorized repository access and sensitive content exposure." -regex = '''ghp_[0-9a-zA-Z]{36}''' -entropy = 3 -keywords = ["ghp_"] -[[rules.allowlists]] -paths = [ - '''(?:^|/)@octokit/auth-token/README\.md$''', -] - -[[rules]] -id = "github-refresh-token" -description = "Detected a GitHub Refresh Token, which could allow prolonged unauthorized access to GitHub services." -regex = '''ghr_[0-9a-zA-Z]{36}''' -entropy = 3 -keywords = ["ghr_"] - -[[rules]] -id = "gitlab-cicd-job-token" -description = "Identified a GitLab CI/CD Job Token, potential access to projects and some APIs on behalf of a user while the CI job is running." -regex = '''glcbt-[0-9a-zA-Z]{1,5}_[0-9a-zA-Z_-]{20}''' -entropy = 3 -keywords = ["glcbt-"] - -[[rules]] -id = "gitlab-deploy-token" -description = "Identified a GitLab Deploy Token, risking access to repositories, packages and containers with write access." -regex = '''gldt-[0-9a-zA-Z_\-]{20}''' -entropy = 3 -keywords = ["gldt-"] - -[[rules]] -id = "gitlab-feature-flag-client-token" -description = "Identified a GitLab feature flag client token, risks exposing user lists and features flags used by an application." -regex = '''glffct-[0-9a-zA-Z_\-]{20}''' -entropy = 3 -keywords = ["glffct-"] - -[[rules]] -id = "gitlab-feed-token" -description = "Identified a GitLab feed token, risking exposure of user data." -regex = '''glft-[0-9a-zA-Z_\-]{20}''' -entropy = 3 -keywords = ["glft-"] - -[[rules]] -id = "gitlab-incoming-mail-token" -description = "Identified a GitLab incoming mail token, risking manipulation of data sent by mail." -regex = '''glimt-[0-9a-zA-Z_\-]{25}''' -entropy = 3 -keywords = ["glimt-"] - -[[rules]] -id = "gitlab-kubernetes-agent-token" -description = "Identified a GitLab Kubernetes Agent token, risking access to repos and registry of projects connected via agent." -regex = '''glagent-[0-9a-zA-Z_\-]{50}''' -entropy = 3 -keywords = ["glagent-"] - -[[rules]] -id = "gitlab-oauth-app-secret" -description = "Identified a GitLab OIDC Application Secret, risking access to apps using GitLab as authentication provider." -regex = '''gloas-[0-9a-zA-Z_\-]{64}''' -entropy = 3 -keywords = ["gloas-"] - -[[rules]] -id = "gitlab-pat" -description = "Identified a GitLab Personal Access Token, risking unauthorized access to GitLab repositories and codebase exposure." -regex = '''glpat-[\w-]{20}''' -entropy = 3 -keywords = ["glpat-"] - -[[rules]] -id = "gitlab-pat-routable" -description = "Identified a GitLab Personal Access Token (routable), risking unauthorized access to GitLab repositories and codebase exposure." -regex = '''\bglpat-[0-9a-zA-Z_-]{27,300}\.[0-9a-z]{2}[0-9a-z]{7}\b''' -entropy = 4 -keywords = ["glpat-"] - -[[rules]] -id = "gitlab-ptt" -description = "Found a GitLab Pipeline Trigger Token, potentially compromising continuous integration workflows and project security." -regex = '''glptt-[0-9a-f]{40}''' -entropy = 3 -keywords = ["glptt-"] - -[[rules]] -id = "gitlab-rrt" -description = "Discovered a GitLab Runner Registration Token, posing a risk to CI/CD pipeline integrity and unauthorized access." -regex = '''GR1348941[\w-]{20}''' -entropy = 3 -keywords = ["gr1348941"] - -[[rules]] -id = "gitlab-runner-authentication-token" -description = "Discovered a GitLab Runner Authentication Token, posing a risk to CI/CD pipeline integrity and unauthorized access." -regex = '''glrt-[0-9a-zA-Z_\-]{20}''' -entropy = 3 -keywords = ["glrt-"] - -[[rules]] -id = "gitlab-runner-authentication-token-routable" -description = "Discovered a GitLab Runner Authentication Token (Routable), posing a risk to CI/CD pipeline integrity and unauthorized access." -regex = '''\bglrt-t\d_[0-9a-zA-Z_\-]{27,300}\.[0-9a-z]{2}[0-9a-z]{7}\b''' -entropy = 4 -keywords = ["glrt-"] - -[[rules]] -id = "gitlab-scim-token" -description = "Discovered a GitLab SCIM Token, posing a risk to unauthorized access for a organization or instance." -regex = '''glsoat-[0-9a-zA-Z_\-]{20}''' -entropy = 3 -keywords = ["glsoat-"] - -[[rules]] -id = "gitlab-session-cookie" -description = "Discovered a GitLab Session Cookie, posing a risk to unauthorized access to a user account." -regex = '''_gitlab_session=[0-9a-z]{32}''' -entropy = 3 -keywords = ["_gitlab_session="] - -[[rules]] -id = "gitter-access-token" -description = "Uncovered a Gitter Access Token, which may lead to unauthorized access to chat and communication services." -regex = '''(?i)[\w.-]{0,50}?(?:gitter)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9_-]{40})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["gitter"] - -[[rules]] -id = "gocardless-api-token" -description = "Detected a GoCardless API token, potentially risking unauthorized direct debit payment operations and financial data exposure." -regex = '''(?i)[\w.-]{0,50}?(?:gocardless)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}(live_(?i)[a-z0-9\-_=]{40})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = [ - "live_", - "gocardless", -] - -[[rules]] -id = "grafana-api-key" -description = "Identified a Grafana API key, which could compromise monitoring dashboards and sensitive data analytics." -regex = '''(?i)\b(eyJrIjoi[A-Za-z0-9]{70,400}={0,3})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = ["eyjrijoi"] - -[[rules]] -id = "grafana-cloud-api-token" -description = "Found a Grafana cloud API token, risking unauthorized access to cloud-based monitoring services and data exposure." -regex = '''(?i)\b(glc_[A-Za-z0-9+/]{32,400}={0,3})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = ["glc_"] - -[[rules]] -id = "grafana-service-account-token" -description = "Discovered a Grafana service account token, posing a risk of compromised monitoring services and data integrity." -regex = '''(?i)\b(glsa_[A-Za-z0-9]{32}_[A-Fa-f0-9]{8})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = ["glsa_"] - -[[rules]] -id = "harness-api-key" -description = "Identified a Harness Access Token (PAT or SAT), risking unauthorized access to a Harness account." -regex = '''(?:pat|sat)\.[a-zA-Z0-9_-]{22}\.[a-zA-Z0-9]{24}\.[a-zA-Z0-9]{20}''' -keywords = [ - "pat.", - "sat.", -] - -[[rules]] -id = "hashicorp-tf-api-token" -description = "Uncovered a HashiCorp Terraform user/org API token, which may lead to unauthorized infrastructure management and security breaches." -regex = '''(?i)[a-z0-9]{14}\.(?-i:atlasv1)\.[a-z0-9\-_=]{60,70}''' -entropy = 3.5 -keywords = ["atlasv1"] - -[[rules]] -id = "hashicorp-tf-password" -description = "Identified a HashiCorp Terraform password field, risking unauthorized infrastructure configuration and security breaches." -regex = '''(?i)[\w.-]{0,50}?(?:administrator_login_password|password)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}("[a-z0-9=_\-]{8,20}")(?:[\x60'"\s;]|\\[nr]|$)''' -path = '''(?i)\.(?:tf|hcl)$''' -entropy = 2 -keywords = [ - "administrator_login_password", - "password", -] - -[[rules]] -id = "heroku-api-key" -description = "Detected a Heroku API Key, potentially compromising cloud application deployments and operational security." -regex = '''(?i)[\w.-]{0,50}?(?:heroku)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["heroku"] - -[[rules]] -id = "hubspot-api-key" -description = "Found a HubSpot API Token, posing a risk to CRM data integrity and unauthorized marketing operations." -regex = '''(?i)[\w.-]{0,50}?(?:hubspot)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["hubspot"] - -[[rules]] -id = "huggingface-access-token" -description = "Discovered a Hugging Face Access token, which could lead to unauthorized access to AI models and sensitive data." -regex = '''\b(hf_(?i:[a-z]{34}))(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = ["hf_"] - -[[rules]] -id = "huggingface-organization-api-token" -description = "Uncovered a Hugging Face Organization API token, potentially compromising AI organization accounts and associated data." -regex = '''\b(api_org_(?i:[a-z]{34}))(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = ["api_org_"] - -[[rules]] -id = "infracost-api-token" -description = "Detected an Infracost API Token, risking unauthorized access to cloud cost estimation tools and financial data." -regex = '''\b(ico-[a-zA-Z0-9]{32})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = ["ico-"] - -[[rules]] -id = "intercom-api-key" -description = "Identified an Intercom API Token, which could compromise customer communication channels and data privacy." -regex = '''(?i)[\w.-]{0,50}?(?:intercom)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9=_\-]{60})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["intercom"] - -[[rules]] -id = "intra42-client-secret" -description = "Found a Intra42 client secret, which could lead to unauthorized access to the 42School API and sensitive data." -regex = '''\b(s-s4t2(?:ud|af)-(?i)[abcdef0123456789]{64})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = [ - "intra", - "s-s4t2ud-", - "s-s4t2af-", -] - -[[rules]] -id = "jfrog-api-key" -description = "Found a JFrog API Key, posing a risk of unauthorized access to software artifact repositories and build pipelines." -regex = '''(?i)[\w.-]{0,50}?(?:jfrog|artifactory|bintray|xray)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{73})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = [ - "jfrog", - "artifactory", - "bintray", - "xray", -] - -[[rules]] -id = "jfrog-identity-token" -description = "Discovered a JFrog Identity Token, potentially compromising access to JFrog services and sensitive software artifacts." -regex = '''(?i)[\w.-]{0,50}?(?:jfrog|artifactory|bintray|xray)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{64})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = [ - "jfrog", - "artifactory", - "bintray", - "xray", -] - -[[rules]] -id = "jwt" -description = "Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data." -regex = '''\b(ey[a-zA-Z0-9]{17,}\.ey[a-zA-Z0-9\/\\_-]{17,}\.(?:[a-zA-Z0-9\/\\_-]{10,}={0,2})?)(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = ["ey"] - -[[rules]] -id = "jwt-base64" -description = "Detected a Base64-encoded JSON Web Token, posing a risk of exposing encoded authentication and data exchange information." -regex = '''\bZXlK(?:(?PaGJHY2lPaU)|(?PaGNIVWlPaU)|(?PaGNIWWlPaU)|(?PaGRXUWlPaU)|(?PaU5qUWlP)|(?PamNtbDBJanBi)|(?PamRIa2lPaU)|(?PbGNHc2lPbn)|(?PbGJtTWlPaU)|(?PcWEzVWlPaU)|(?PcWQyc2lPb)|(?PcGMzTWlPaU)|(?PcGRpSTZJ)|(?PcmFXUWlP)|(?PclpYbGZiM0J6SWpwY)|(?PcmRIa2lPaUp)|(?PdWIyNWpaU0k2)|(?Pd01tTWlP)|(?Pd01uTWlPaU)|(?Pd2NIUWlPaU)|(?PemRXSWlPaU)|(?PemRuUWlP)|(?PMFlXY2lPaU)|(?PMGVYQWlPaUp)|(?PMWNtd2l)|(?PMWMyVWlPaUp)|(?PMlpYSWlPaU)|(?PMlpYSnphVzl1SWpv)|(?PNElqb2)|(?PNE5XTWlP)|(?PNE5YUWlPaU)|(?PNE5YUWpVekkxTmlJNkl)|(?PNE5YVWlPaU)|(?PNmFYQWlPaU))[a-zA-Z0-9\/\\_+\-\r\n]{40,}={0,2}''' -entropy = 2 -keywords = ["zxlk"] - -[[rules]] -id = "kraken-access-token" -description = "Identified a Kraken Access Token, potentially compromising cryptocurrency trading accounts and financial security." -regex = '''(?i)[\w.-]{0,50}?(?:kraken)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9\/=_\+\-]{80,90})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["kraken"] - -[[rules]] -id = "kubernetes-secret-yaml" -description = "Possible Kubernetes Secret detected, posing a risk of leaking credentials/tokens from your deployments" -regex = '''(?i)(?:\bkind:[ \t]*["']?\bsecret\b["']?(?s:.){0,200}?\bdata:(?s:.){0,100}?\s+([\w.-]+:(?:[ \t]*(?:\||>[-+]?)\s+)?[ \t]*(?:["']?[a-z0-9+/]{10,}={0,3}["']?|\{\{[ \t\w"|$:=,.-]+}}|""|''))|\bdata:(?s:.){0,100}?\s+([\w.-]+:(?:[ \t]*(?:\||>[-+]?)\s+)?[ \t]*(?:["']?[a-z0-9+/]{10,}={0,3}["']?|\{\{[ \t\w"|$:=,.-]+}}|""|''))(?s:.){0,200}?\bkind:[ \t]*["']?\bsecret\b["']?)''' -path = '''(?i)\.ya?ml$''' -keywords = ["secret"] -[[rules.allowlists]] -regexes = [ - '''[\w.-]+:(?:[ \t]*(?:\||>[-+]?)\s+)?[ \t]*(?:\{\{[ \t\w"|$:=,.-]+}}|""|'')''', -] -[[rules.allowlists]] -regexTarget = "match" -regexes = [ - '''(kind:(?s:.)+\n---\n(?s:.)+\bdata:|data:(?s:.)+\n---\n(?s:.)+\bkind:)''', -] - -[[rules]] -id = "kucoin-access-token" -description = "Found a Kucoin Access Token, risking unauthorized access to cryptocurrency exchange services and transactions." -regex = '''(?i)[\w.-]{0,50}?(?:kucoin)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-f0-9]{24})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["kucoin"] - -[[rules]] -id = "kucoin-secret-key" -description = "Discovered a Kucoin Secret Key, which could lead to compromised cryptocurrency operations and financial data breaches." -regex = '''(?i)[\w.-]{0,50}?(?:kucoin)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["kucoin"] - -[[rules]] -id = "launchdarkly-access-token" -description = "Uncovered a Launchdarkly Access Token, potentially compromising feature flag management and application functionality." -regex = '''(?i)[\w.-]{0,50}?(?:launchdarkly)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9=_\-]{40})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["launchdarkly"] - -[[rules]] -id = "linear-api-key" -description = "Detected a Linear API Token, posing a risk to project management tools and sensitive task data." -regex = '''lin_api_(?i)[a-z0-9]{40}''' -entropy = 2 -keywords = ["lin_api_"] - -[[rules]] -id = "linear-client-secret" -description = "Identified a Linear Client Secret, which may compromise secure integrations and sensitive project management data." -regex = '''(?i)[\w.-]{0,50}?(?:linear)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-f0-9]{32})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = ["linear"] - -[[rules]] -id = "linkedin-client-id" -description = "Found a LinkedIn Client ID, risking unauthorized access to LinkedIn integrations and professional data exposure." -regex = '''(?i)[\w.-]{0,50}?(?:linked[_-]?in)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{14})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = [ - "linkedin", - "linked_in", - "linked-in", -] - -[[rules]] -id = "linkedin-client-secret" -description = "Discovered a LinkedIn Client secret, potentially compromising LinkedIn application integrations and user data." -regex = '''(?i)[\w.-]{0,50}?(?:linked[_-]?in)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{16})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = [ - "linkedin", - "linked_in", - "linked-in", -] - -[[rules]] -id = "lob-api-key" -description = "Uncovered a Lob API Key, which could lead to unauthorized access to mailing and address verification services." -regex = '''(?i)[\w.-]{0,50}?(?:lob)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}((live|test)_[a-f0-9]{35})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = [ - "test_", - "live_", -] - -[[rules]] -id = "lob-pub-api-key" -description = "Detected a Lob Publishable API Key, posing a risk of exposing mail and print service integrations." -regex = '''(?i)[\w.-]{0,50}?(?:lob)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}((test|live)_pub_[a-f0-9]{31})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = [ - "test_pub", - "live_pub", - "_pub", -] - -[[rules]] -id = "mailchimp-api-key" -description = "Identified a Mailchimp API key, potentially compromising email marketing campaigns and subscriber data." -regex = '''(?i)[\w.-]{0,50}?(?:MailchimpSDK.initialize|mailchimp)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-f0-9]{32}-us\d\d)(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["mailchimp"] - -[[rules]] -id = "mailgun-private-api-token" -description = "Found a Mailgun private API token, risking unauthorized email service operations and data breaches." -regex = '''(?i)[\w.-]{0,50}?(?:mailgun)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}(key-[a-f0-9]{32})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["mailgun"] - -[[rules]] -id = "mailgun-pub-key" -description = "Discovered a Mailgun public validation key, which could expose email verification processes and associated data." -regex = '''(?i)[\w.-]{0,50}?(?:mailgun)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}(pubkey-[a-f0-9]{32})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["mailgun"] - -[[rules]] -id = "mailgun-signing-key" -description = "Uncovered a Mailgun webhook signing key, potentially compromising email automation and data integrity." -regex = '''(?i)[\w.-]{0,50}?(?:mailgun)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-h0-9]{32}-[a-h0-9]{8}-[a-h0-9]{8})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["mailgun"] - -[[rules]] -id = "mapbox-api-token" -description = "Detected a MapBox API token, posing a risk to geospatial services and sensitive location data exposure." -regex = '''(?i)[\w.-]{0,50}?(?:mapbox)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}(pk\.[a-z0-9]{60}\.[a-z0-9]{22})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["mapbox"] - -[[rules]] -id = "mattermost-access-token" -description = "Identified a Mattermost Access Token, which may compromise team communication channels and data privacy." -regex = '''(?i)[\w.-]{0,50}?(?:mattermost)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{26})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["mattermost"] - -[[rules]] -id = "maxmind-license-key" -description = "Discovered a potential MaxMind license key." -regex = '''\b([A-Za-z0-9]{6}_[A-Za-z0-9]{29}_mmk)(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 4 -keywords = ["_mmk"] - -[[rules]] -id = "messagebird-api-token" -description = "Found a MessageBird API token, risking unauthorized access to communication platforms and message data." -regex = '''(?i)[\w.-]{0,50}?(?:message[_-]?bird)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{25})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = [ - "messagebird", - "message-bird", - "message_bird", -] - -[[rules]] -id = "messagebird-client-id" -description = "Discovered a MessageBird client ID, potentially compromising API integrations and sensitive communication data." -regex = '''(?i)[\w.-]{0,50}?(?:message[_-]?bird)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = [ - "messagebird", - "message-bird", - "message_bird", -] - -[[rules]] -id = "microsoft-teams-webhook" -description = "Uncovered a Microsoft Teams Webhook, which could lead to unauthorized access to team collaboration tools and data leaks." -regex = '''https://[a-z0-9]+\.webhook\.office\.com/webhookb2/[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}@[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}/IncomingWebhook/[a-z0-9]{32}/[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}''' -keywords = [ - "webhook.office.com", - "webhookb2", - "incomingwebhook", -] - -[[rules]] -id = "netlify-access-token" -description = "Detected a Netlify Access Token, potentially compromising web hosting services and site management." -regex = '''(?i)[\w.-]{0,50}?(?:netlify)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9=_\-]{40,46})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["netlify"] - -[[rules]] -id = "new-relic-browser-api-token" -description = "Identified a New Relic ingest browser API token, risking unauthorized access to application performance data and analytics." -regex = '''(?i)[\w.-]{0,50}?(?:new-relic|newrelic|new_relic)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}(NRJS-[a-f0-9]{19})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["nrjs-"] - -[[rules]] -id = "new-relic-insert-key" -description = "Discovered a New Relic insight insert key, compromising data injection into the platform." -regex = '''(?i)[\w.-]{0,50}?(?:new-relic|newrelic|new_relic)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}(NRII-[a-z0-9-]{32})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["nrii-"] - -[[rules]] -id = "new-relic-user-api-id" -description = "Found a New Relic user API ID, posing a risk to application monitoring services and data integrity." -regex = '''(?i)[\w.-]{0,50}?(?:new-relic|newrelic|new_relic)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{64})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = [ - "new-relic", - "newrelic", - "new_relic", -] - -[[rules]] -id = "new-relic-user-api-key" -description = "Discovered a New Relic user API Key, which could lead to compromised application insights and performance monitoring." -regex = '''(?i)[\w.-]{0,50}?(?:new-relic|newrelic|new_relic)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}(NRAK-[a-z0-9]{27})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["nrak"] - -[[rules]] -id = "npm-access-token" -description = "Uncovered an npm access token, potentially compromising package management and code repository access." -regex = '''(?i)\b(npm_[a-z0-9]{36})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = ["npm_"] - -[[rules]] -id = "nuget-config-password" -description = "Identified a password within a Nuget config file, potentially compromising package management access." -regex = '''(?i)''' -path = '''(?i)nuget\.config$''' -entropy = 1 -keywords = ["|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9=_\-]{32})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = [ - "nytimes", - "new-york-times", - "newyorktimes", -] - -[[rules]] -id = "octopus-deploy-api-key" -description = "Discovered a potential Octopus Deploy API key, risking application deployments and operational security." -regex = '''\b(API-[A-Z0-9]{26})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = ["api-"] - -[[rules]] -id = "okta-access-token" -description = "Identified an Okta Access Token, which may compromise identity management services and user authentication data." -regex = '''[\w.-]{0,50}?(?i:[\w.-]{0,50}?(?:(?-i:[Oo]kta|OKTA))(?:[ \t\w.-]{0,20})[\s'"]{0,3})(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}(00[\w=\-]{40})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 4 -keywords = ["okta"] - -[[rules]] -id = "openai-api-key" -description = "Found an OpenAI API Key, posing a risk of unauthorized access to AI services and data manipulation." -regex = '''\b(sk-(?:proj|svcacct|admin)-(?:[A-Za-z0-9_-]{74}|[A-Za-z0-9_-]{58})T3BlbkFJ(?:[A-Za-z0-9_-]{74}|[A-Za-z0-9_-]{58})\b|sk-[a-zA-Z0-9]{20}T3BlbkFJ[a-zA-Z0-9]{20})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = ["t3blbkfj"] - -[[rules]] -id = "openshift-user-token" -description = "Found an OpenShift user token, potentially compromising an OpenShift/Kubernetes cluster." -regex = '''\b(sha256~[\w-]{43})(?:[^\w-]|\z)''' -entropy = 3.5 -keywords = ["sha256~"] - -[[rules]] -id = "perplexity-api-key" -description = "Detected a Perplexity API key, which could lead to unauthorized access to Perplexity AI services and data exposure." -regex = '''\b(pplx-[a-zA-Z0-9]{48})(?:[\x60'"\s;]|\\[nr]|$|\b)''' -entropy = 4 -keywords = ["pplx-"] - -[[rules]] -id = "pkcs12-file" -description = "Found a PKCS #12 file, which commonly contain bundled private keys." -path = '''(?i)(?:^|\/)[^\/]+\.p(?:12|fx)$''' - -[[rules]] -id = "plaid-api-token" -description = "Discovered a Plaid API Token, potentially compromising financial data aggregation and banking services." -regex = '''(?i)[\w.-]{0,50}?(?:plaid)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}(access-(?:sandbox|development|production)-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["plaid"] - -[[rules]] -id = "plaid-client-id" -description = "Uncovered a Plaid Client ID, which could lead to unauthorized financial service integrations and data breaches." -regex = '''(?i)[\w.-]{0,50}?(?:plaid)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{24})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3.5 -keywords = ["plaid"] - -[[rules]] -id = "plaid-secret-key" -description = "Detected a Plaid Secret key, risking unauthorized access to financial accounts and sensitive transaction data." -regex = '''(?i)[\w.-]{0,50}?(?:plaid)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{30})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3.5 -keywords = ["plaid"] - -[[rules]] -id = "planetscale-api-token" -description = "Identified a PlanetScale API token, potentially compromising database management and operations." -regex = '''\b(pscale_tkn_(?i)[\w=\.-]{32,64})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = ["pscale_tkn_"] - -[[rules]] -id = "planetscale-oauth-token" -description = "Found a PlanetScale OAuth token, posing a risk to database access control and sensitive data integrity." -regex = '''\b(pscale_oauth_[\w=\.-]{32,64})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = ["pscale_oauth_"] - -[[rules]] -id = "planetscale-password" -description = "Discovered a PlanetScale password, which could lead to unauthorized database operations and data breaches." -regex = '''(?i)\b(pscale_pw_(?i)[\w=\.-]{32,64})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = ["pscale_pw_"] - -[[rules]] -id = "postman-api-token" -description = "Uncovered a Postman API token, potentially compromising API testing and development workflows." -regex = '''\b(PMAK-(?i)[a-f0-9]{24}\-[a-f0-9]{34})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = ["pmak-"] - -[[rules]] -id = "prefect-api-token" -description = "Detected a Prefect API token, risking unauthorized access to workflow management and automation services." -regex = '''\b(pnu_[a-zA-Z0-9]{36})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = ["pnu_"] - -[[rules]] -id = "private-key" -description = "Identified a Private Key, which may compromise cryptographic security and sensitive data encryption." -regex = '''(?i)-----BEGIN[ A-Z0-9_-]{0,100}PRIVATE KEY(?: BLOCK)?-----[\s\S-]{64,}?KEY(?: BLOCK)?-----''' -keywords = ["-----begin"] - -[[rules]] -id = "privateai-api-token" -description = "Identified a PrivateAI Token, posing a risk of unauthorized access to AI services and data manipulation." -regex = '''[\w.-]{0,50}?(?i:[\w.-]{0,50}?(?:private[_-]?ai)(?:[ \t\w.-]{0,20})[\s'"]{0,3})(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{32})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = [ - "privateai", - "private_ai", - "private-ai", -] - -[[rules]] -id = "pulumi-api-token" -description = "Found a Pulumi API token, posing a risk to infrastructure as code services and cloud resource management." -regex = '''\b(pul-[a-f0-9]{40})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = ["pul-"] - -[[rules]] -id = "pypi-upload-token" -description = "Discovered a PyPI upload token, potentially compromising Python package distribution and repository integrity." -regex = '''pypi-AgEIcHlwaS5vcmc[\w-]{50,1000}''' -entropy = 3 -keywords = ["pypi-ageichlwas5vcmc"] - -[[rules]] -id = "rapidapi-access-token" -description = "Uncovered a RapidAPI Access Token, which could lead to unauthorized access to various APIs and data services." -regex = '''(?i)[\w.-]{0,50}?(?:rapidapi)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9_-]{50})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["rapidapi"] - -[[rules]] -id = "readme-api-token" -description = "Detected a Readme API token, risking unauthorized documentation management and content exposure." -regex = '''\b(rdme_[a-z0-9]{70})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = ["rdme_"] - -[[rules]] -id = "rubygems-api-token" -description = "Identified a Rubygem API token, potentially compromising Ruby library distribution and package management." -regex = '''\b(rubygems_[a-f0-9]{48})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = ["rubygems_"] - -[[rules]] -id = "scalingo-api-token" -description = "Found a Scalingo API token, posing a risk to cloud platform services and application deployment security." -regex = '''\b(tk-us-[\w-]{48})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = ["tk-us-"] - -[[rules]] -id = "sendbird-access-id" -description = "Discovered a Sendbird Access ID, which could compromise chat and messaging platform integrations." -regex = '''(?i)[\w.-]{0,50}?(?:sendbird)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["sendbird"] - -[[rules]] -id = "sendbird-access-token" -description = "Uncovered a Sendbird Access Token, potentially risking unauthorized access to communication services and user data." -regex = '''(?i)[\w.-]{0,50}?(?:sendbird)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-f0-9]{40})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["sendbird"] - -[[rules]] -id = "sendgrid-api-token" -description = "Detected a SendGrid API token, posing a risk of unauthorized email service operations and data exposure." -regex = '''\b(SG\.(?i)[a-z0-9=_\-\.]{66})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = ["sg."] - -[[rules]] -id = "sendinblue-api-token" -description = "Identified a Sendinblue API token, which may compromise email marketing services and subscriber data privacy." -regex = '''\b(xkeysib-[a-f0-9]{64}\-(?i)[a-z0-9]{16})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = ["xkeysib-"] - -[[rules]] -id = "sentry-access-token" -description = "Found a Sentry.io Access Token (old format), risking unauthorized access to error tracking services and sensitive application data." -regex = '''(?i)[\w.-]{0,50}?(?:sentry)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-f0-9]{64})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = ["sentry"] - -[[rules]] -id = "sentry-org-token" -description = "Found a Sentry.io Organization Token, risking unauthorized access to error tracking services and sensitive application data." -regex = '''\bsntrys_eyJpYXQiO[a-zA-Z0-9+/]{10,200}(?:LCJyZWdpb25fdXJs|InJlZ2lvbl91cmwi|cmVnaW9uX3VybCI6)[a-zA-Z0-9+/]{10,200}={0,2}_[a-zA-Z0-9+/]{43}(?:[^a-zA-Z0-9+/]|\z)''' -entropy = 4.5 -keywords = ["sntrys_eyjpyxqio"] - -[[rules]] -id = "sentry-user-token" -description = "Found a Sentry.io User Token, risking unauthorized access to error tracking services and sensitive application data." -regex = '''\b(sntryu_[a-f0-9]{64})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3.5 -keywords = ["sntryu_"] - -[[rules]] -id = "settlemint-application-access-token" -description = "Found a Settlemint Application Access Token." -regex = '''\b(sm_aat_[a-zA-Z0-9]{16})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = ["sm_aat"] - -[[rules]] -id = "settlemint-personal-access-token" -description = "Found a Settlemint Personal Access Token." -regex = '''\b(sm_pat_[a-zA-Z0-9]{16})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = ["sm_pat"] - -[[rules]] -id = "settlemint-service-access-token" -description = "Found a Settlemint Service Access Token." -regex = '''\b(sm_sat_[a-zA-Z0-9]{16})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = ["sm_sat"] - -[[rules]] -id = "shippo-api-token" -description = "Discovered a Shippo API token, potentially compromising shipping services and customer order data." -regex = '''\b(shippo_(?:live|test)_[a-fA-F0-9]{40})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = ["shippo_"] - -[[rules]] -id = "shopify-access-token" -description = "Uncovered a Shopify access token, which could lead to unauthorized e-commerce platform access and data breaches." -regex = '''shpat_[a-fA-F0-9]{32}''' -entropy = 2 -keywords = ["shpat_"] - -[[rules]] -id = "shopify-custom-access-token" -description = "Detected a Shopify custom access token, potentially compromising custom app integrations and e-commerce data security." -regex = '''shpca_[a-fA-F0-9]{32}''' -entropy = 2 -keywords = ["shpca_"] - -[[rules]] -id = "shopify-private-app-access-token" -description = "Identified a Shopify private app access token, risking unauthorized access to private app data and store operations." -regex = '''shppa_[a-fA-F0-9]{32}''' -entropy = 2 -keywords = ["shppa_"] - -[[rules]] -id = "shopify-shared-secret" -description = "Found a Shopify shared secret, posing a risk to application authentication and e-commerce platform security." -regex = '''shpss_[a-fA-F0-9]{32}''' -entropy = 2 -keywords = ["shpss_"] - -[[rules]] -id = "sidekiq-secret" -description = "Discovered a Sidekiq Secret, which could lead to compromised background job processing and application data breaches." -regex = '''(?i)[\w.-]{0,50}?(?:BUNDLE_ENTERPRISE__CONTRIBSYS__COM|BUNDLE_GEMS__CONTRIBSYS__COM)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-f0-9]{8}:[a-f0-9]{8})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = [ - "bundle_enterprise__contribsys__com", - "bundle_gems__contribsys__com", -] - -[[rules]] -id = "sidekiq-sensitive-url" -description = "Uncovered a Sidekiq Sensitive URL, potentially exposing internal job queues and sensitive operation details." -regex = '''(?i)\bhttps?://([a-f0-9]{8}:[a-f0-9]{8})@(?:gems.contribsys.com|enterprise.contribsys.com)(?:[\/|\#|\?|:]|$)''' -keywords = [ - "gems.contribsys.com", - "enterprise.contribsys.com", -] - -[[rules]] -id = "slack-app-token" -description = "Detected a Slack App-level token, risking unauthorized access to Slack applications and workspace data." -regex = '''(?i)xapp-\d-[A-Z0-9]+-\d+-[a-z0-9]+''' -entropy = 2 -keywords = ["xapp"] - -[[rules]] -id = "slack-bot-token" -description = "Identified a Slack Bot token, which may compromise bot integrations and communication channel security." -regex = '''xoxb-[0-9]{10,13}-[0-9]{10,13}[a-zA-Z0-9-]*''' -entropy = 3 -keywords = ["xoxb"] - -[[rules]] -id = "slack-config-access-token" -description = "Found a Slack Configuration access token, posing a risk to workspace configuration and sensitive data access." -regex = '''(?i)xoxe.xox[bp]-\d-[A-Z0-9]{163,166}''' -entropy = 2 -keywords = [ - "xoxe.xoxb-", - "xoxe.xoxp-", -] - -[[rules]] -id = "slack-config-refresh-token" -description = "Discovered a Slack Configuration refresh token, potentially allowing prolonged unauthorized access to configuration settings." -regex = '''(?i)xoxe-\d-[A-Z0-9]{146}''' -entropy = 2 -keywords = ["xoxe-"] - -[[rules]] -id = "slack-legacy-bot-token" -description = "Uncovered a Slack Legacy bot token, which could lead to compromised legacy bot operations and data exposure." -regex = '''xoxb-[0-9]{8,14}-[a-zA-Z0-9]{18,26}''' -entropy = 2 -keywords = ["xoxb"] - -[[rules]] -id = "slack-legacy-token" -description = "Detected a Slack Legacy token, risking unauthorized access to older Slack integrations and user data." -regex = '''xox[os]-\d+-\d+-\d+-[a-fA-F\d]+''' -entropy = 2 -keywords = [ - "xoxo", - "xoxs", -] - -[[rules]] -id = "slack-legacy-workspace-token" -description = "Identified a Slack Legacy Workspace token, potentially compromising access to workspace data and legacy features." -regex = '''xox[ar]-(?:\d-)?[0-9a-zA-Z]{8,48}''' -entropy = 2 -keywords = [ - "xoxa", - "xoxr", -] - -[[rules]] -id = "slack-user-token" -description = "Found a Slack User token, posing a risk of unauthorized user impersonation and data access within Slack workspaces." -regex = '''xox[pe](?:-[0-9]{10,13}){3}-[a-zA-Z0-9-]{28,34}''' -entropy = 2 -keywords = [ - "xoxp-", - "xoxe-", -] - -[[rules]] -id = "slack-webhook-url" -description = "Discovered a Slack Webhook, which could lead to unauthorized message posting and data leakage in Slack channels." -regex = '''(?:https?://)?hooks.slack.com/(?:services|workflows|triggers)/[A-Za-z0-9+/]{43,56}''' -keywords = ["hooks.slack.com"] - -[[rules]] -id = "snyk-api-token" -description = "Uncovered a Snyk API token, potentially compromising software vulnerability scanning and code security." -regex = '''(?i)[\w.-]{0,50}?(?:snyk[_.-]?(?:(?:api|oauth)[_.-]?)?(?:key|token))(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["snyk"] - -[[rules]] -id = "sonar-api-token" -description = "Uncovered a Sonar API token, potentially compromising software vulnerability scanning and code security." -regex = '''(?i)[\w.-]{0,50}?(?:sonar[_.-]?(login|token))(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9=_\-]{40})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["sonar"] - -[[rules]] -id = "sourcegraph-access-token" -description = "Sourcegraph is a code search and navigation engine." -regex = '''(?i)\b(\b(sgp_(?:[a-fA-F0-9]{16}|local)_[a-fA-F0-9]{40}|sgp_[a-fA-F0-9]{40}|[a-fA-F0-9]{40})\b)(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = [ - "sgp_", - "sourcegraph", -] - -[[rules]] -id = "square-access-token" -description = "Detected a Square Access Token, risking unauthorized payment processing and financial transaction exposure." -regex = '''\b((?:EAAA|sq0atp-)[\w-]{22,60})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = [ - "sq0atp-", - "eaaa", -] - -[[rules]] -id = "squarespace-access-token" -description = "Identified a Squarespace Access Token, which may compromise website management and content control on Squarespace." -regex = '''(?i)[\w.-]{0,50}?(?:squarespace)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["squarespace"] - -[[rules]] -id = "stripe-access-token" -description = "Found a Stripe Access Token, posing a risk to payment processing services and sensitive financial data." -regex = '''\b((?:sk|rk)_(?:test|live|prod)_[a-zA-Z0-9]{10,99})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 2 -keywords = [ - "sk_test", - "sk_live", - "sk_prod", - "rk_test", - "rk_live", - "rk_prod", -] - -[[rules]] -id = "sumologic-access-id" -description = "Discovered a SumoLogic Access ID, potentially compromising log management services and data analytics integrity." -regex = '''[\w.-]{0,50}?(?i:[\w.-]{0,50}?(?:(?-i:[Ss]umo|SUMO))(?:[ \t\w.-]{0,20})[\s'"]{0,3})(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}(su[a-zA-Z0-9]{12})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = ["sumo"] - -[[rules]] -id = "sumologic-access-token" -description = "Uncovered a SumoLogic Access Token, which could lead to unauthorized access to log data and analytics insights." -regex = '''(?i)[\w.-]{0,50}?(?:(?-i:[Ss]umo|SUMO))(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{64})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3 -keywords = ["sumo"] - -[[rules]] -id = "telegram-bot-api-token" -description = "Detected a Telegram Bot API Token, risking unauthorized bot operations and message interception on Telegram." -regex = '''(?i)[\w.-]{0,50}?(?:telegr)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([0-9]{5,16}:(?-i:A)[a-z0-9_\-]{34})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["telegr"] - -[[rules]] -id = "travisci-access-token" -description = "Identified a Travis CI Access Token, potentially compromising continuous integration services and codebase security." -regex = '''(?i)[\w.-]{0,50}?(?:travis)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{22})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["travis"] - -[[rules]] -id = "twilio-api-key" -description = "Found a Twilio API Key, posing a risk to communication services and sensitive customer interaction data." -regex = '''SK[0-9a-fA-F]{32}''' -entropy = 3 -keywords = ["sk"] - -[[rules]] -id = "twitch-api-token" -description = "Discovered a Twitch API token, which could compromise streaming services and account integrations." -regex = '''(?i)[\w.-]{0,50}?(?:twitch)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{30})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["twitch"] - -[[rules]] -id = "twitter-access-secret" -description = "Uncovered a Twitter Access Secret, potentially risking unauthorized Twitter integrations and data breaches." -regex = '''(?i)[\w.-]{0,50}?(?:twitter)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{45})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["twitter"] - -[[rules]] -id = "twitter-access-token" -description = "Detected a Twitter Access Token, posing a risk of unauthorized account operations and social media data exposure." -regex = '''(?i)[\w.-]{0,50}?(?:twitter)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([0-9]{15,25}-[a-zA-Z0-9]{20,40})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["twitter"] - -[[rules]] -id = "twitter-api-key" -description = "Identified a Twitter API Key, which may compromise Twitter application integrations and user data security." -regex = '''(?i)[\w.-]{0,50}?(?:twitter)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{25})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["twitter"] - -[[rules]] -id = "twitter-api-secret" -description = "Found a Twitter API Secret, risking the security of Twitter app integrations and sensitive data access." -regex = '''(?i)[\w.-]{0,50}?(?:twitter)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{50})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["twitter"] - -[[rules]] -id = "twitter-bearer-token" -description = "Discovered a Twitter Bearer Token, potentially compromising API access and data retrieval from Twitter." -regex = '''(?i)[\w.-]{0,50}?(?:twitter)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}(A{22}[a-zA-Z0-9%]{80,100})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["twitter"] - -[[rules]] -id = "typeform-api-token" -description = "Uncovered a Typeform API token, which could lead to unauthorized survey management and data collection." -regex = '''(?i)[\w.-]{0,50}?(?:typeform)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}(tfp_[a-z0-9\-_\.=]{59})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["tfp_"] - -[[rules]] -id = "vault-batch-token" -description = "Detected a Vault Batch Token, risking unauthorized access to secret management services and sensitive data." -regex = '''\b(hvb\.[\w-]{138,300})(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 4 -keywords = ["hvb."] - -[[rules]] -id = "vault-service-token" -description = "Identified a Vault Service Token, potentially compromising infrastructure security and access to sensitive credentials." -regex = '''\b((?:hvs\.[\w-]{90,120}|s\.(?i:[a-z0-9]{24})))(?:[\x60'"\s;]|\\[nr]|$)''' -entropy = 3.5 -keywords = [ - "hvs.", - "s.", -] -[[rules.allowlists]] -regexes = [ - '''s\.[A-Za-z]{24}''', -] - -[[rules]] -id = "yandex-access-token" -description = "Found a Yandex Access Token, posing a risk to Yandex service integrations and user data privacy." -regex = '''(?i)[\w.-]{0,50}?(?:yandex)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}(t1\.[A-Z0-9a-z_-]+[=]{0,2}\.[A-Z0-9a-z_-]{86}[=]{0,2})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["yandex"] - -[[rules]] -id = "yandex-api-key" -description = "Discovered a Yandex API Key, which could lead to unauthorized access to Yandex services and data manipulation." -regex = '''(?i)[\w.-]{0,50}?(?:yandex)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}(AQVN[A-Za-z0-9_\-]{35,38})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["yandex"] - -[[rules]] -id = "yandex-aws-access-token" -description = "Uncovered a Yandex AWS Access Token, potentially compromising cloud resource access and data security on Yandex Cloud." -regex = '''(?i)[\w.-]{0,50}?(?:yandex)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}(YC[a-zA-Z0-9_\-]{38})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["yandex"] - -[[rules]] -id = "zendesk-secret-key" -description = "Detected a Zendesk Secret Key, risking unauthorized access to customer support services and sensitive ticketing data." -regex = '''(?i)[\w.-]{0,50}?(?:zendesk)(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|>|:{1,3}=|\|\||:|=>|\?=|,)[\x60'"\s=]{0,5}([a-z0-9]{40})(?:[\x60'"\s;]|\\[nr]|$)''' -keywords = ["zendesk"] - diff --git a/cli/detect/config/rule.go b/cli/detect/config/rule.go deleted file mode 100644 index 6d2b61326..000000000 --- a/cli/detect/config/rule.go +++ /dev/null @@ -1,114 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package config - -import ( - "fmt" - "strings" - - "github.com/Infisical/infisical-merge/detect/regexp" -) - -// Rules contain information that define details on how to detect secrets -type Rule struct { - // RuleID is a unique identifier for this rule - RuleID string - - // Description is the description of the rule. - Description string - - // Entropy is a float representing the minimum shannon - // entropy a regex group must have to be considered a secret. - Entropy float64 - - // SecretGroup is an int used to extract secret from regex - // match and used as the group that will have its entropy - // checked if `entropy` is set. - SecretGroup int - - // Regex is a golang regular expression used to detect secrets. - Regex *regexp.Regexp - - // Path is a golang regular expression used to - // filter secrets by path - Path *regexp.Regexp - - // Tags is an array of strings used for metadata - // and reporting purposes. - Tags []string - - // Keywords are used for pre-regex check filtering. Rules that contain - // keywords will perform a quick string compare check to make sure the - // keyword(s) are in the content being scanned. - Keywords []string - - // Allowlists allows a rule to be ignored for specific commits, paths, regexes, and/or stopwords. - Allowlists []*Allowlist - - // validated is an internal flag to track whether `Validate()` has been called. - validated bool -} - -// Validate guards against common misconfigurations. -func (r *Rule) Validate() error { - if r.validated { - return nil - } - - // Ensure |id| is present. - if strings.TrimSpace(r.RuleID) == "" { - // Try to provide helpful context, since |id| is empty. - var context string - if r.Regex != nil { - context = ", regex: " + r.Regex.String() - } else if r.Path != nil { - context = ", path: " + r.Path.String() - } else if r.Description != "" { - context = ", description: " + r.Description - } - return fmt.Errorf("rule |id| is missing or empty" + context) - } - - // Ensure the rule actually matches something. - if r.Regex == nil && r.Path == nil { - return fmt.Errorf("%s: both |regex| and |path| are empty, this rule will have no effect", r.RuleID) - } - - // Ensure |secretGroup| works. - if r.Regex != nil && r.SecretGroup > r.Regex.NumSubexp() { - return fmt.Errorf("%s: invalid regex secret group %d, max regex secret group %d", r.RuleID, r.SecretGroup, r.Regex.NumSubexp()) - } - - for _, allowlist := range r.Allowlists { - // This will probably never happen. - if allowlist == nil { - continue - } - if err := allowlist.Validate(); err != nil { - return fmt.Errorf("%s: %w", r.RuleID, err) - } - } - - r.validated = true - return nil -} diff --git a/cli/detect/config/utils.go b/cli/detect/config/utils.go deleted file mode 100644 index e28a5cb37..000000000 --- a/cli/detect/config/utils.go +++ /dev/null @@ -1,46 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package config - -import ( - "github.com/Infisical/infisical-merge/detect/regexp" -) - -func anyRegexMatch(f string, res []*regexp.Regexp) bool { - for _, re := range res { - if regexMatched(f, re) { - return true - } - } - return false -} - -func regexMatched(f string, re *regexp.Regexp) bool { - if re == nil { - return false - } - if re.FindString(f) != "" { - return true - } - return false -} diff --git a/cli/detect/decoder.go b/cli/detect/decoder.go deleted file mode 100644 index 6ec509757..000000000 --- a/cli/detect/decoder.go +++ /dev/null @@ -1,328 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package detect - -import ( - "bytes" - "encoding/base64" - "fmt" - "regexp" - "unicode" - - "github.com/Infisical/infisical-merge/detect/logging" -) - -var b64LikelyChars [128]byte -var b64Regexp = regexp.MustCompile(`[\w/+-]{16,}={0,3}`) -var decoders = []func(string) ([]byte, error){ - base64.StdEncoding.DecodeString, - base64.RawURLEncoding.DecodeString, -} - -func init() { - // Basically look for anything that isn't just letters - for _, c := range `0123456789+/-_` { - b64LikelyChars[c] = 1 - } -} - -// EncodedSegment represents a portion of text that is encoded in some way. -// `decode` supports recusive decoding and can result in "segment trees". -// There can be multiple segments in the original text, so each can be thought -// of as its own tree with the root being the original segment. -type EncodedSegment struct { - // The parent segment in a segment tree. If nil, it is a root segment - parent *EncodedSegment - - // Relative start/end are the bounds of the encoded value in the current pass. - relativeStart int - relativeEnd int - - // Absolute start/end refer to the bounds of the root segment in this segment - // tree - absoluteStart int - absoluteEnd int - - // Decoded start/end refer to the bounds of the decoded value in the current - // pass. These can differ from relative values because decoding can shrink - // or grow the size of the segment. - decodedStart int - decodedEnd int - - // This is the actual decoded content in the segment - decodedValue string - - // This is the type of encoding - encoding string -} - -// isChildOf inspects the bounds of two segments to determine -// if one should be the child of another -func (s EncodedSegment) isChildOf(parent EncodedSegment) bool { - return parent.decodedStart <= s.relativeStart && parent.decodedEnd >= s.relativeEnd -} - -// decodedOverlaps checks if the decoded bounds of the segment overlaps a range -func (s EncodedSegment) decodedOverlaps(start, end int) bool { - return start <= s.decodedEnd && end >= s.decodedStart -} - -// adjustMatchIndex takes the matchIndex from the current decoding pass and -// updates it to match the absolute matchIndex in the original text. -func (s EncodedSegment) adjustMatchIndex(matchIndex []int) []int { - // The match is within the bounds of the segment so we just return - // the absolute start and end of the root segment. - if s.decodedStart <= matchIndex[0] && matchIndex[1] <= s.decodedEnd { - return []int{ - s.absoluteStart, - s.absoluteEnd, - } - } - - // Since it overlaps one side and/or the other, we're going to have to adjust - // and climb parents until we're either at the root or we've determined - // we're fully inside one of the parent segments. - adjustedMatchIndex := make([]int, 2) - - if matchIndex[0] < s.decodedStart { - // It starts before the encoded segment so adjust the start to match - // the location before it was decoded - matchStartDelta := s.decodedStart - matchIndex[0] - adjustedMatchIndex[0] = s.relativeStart - matchStartDelta - } else { - // It starts within the encoded segment so set the bound to the - // relative start - adjustedMatchIndex[0] = s.relativeStart - } - - if matchIndex[1] > s.decodedEnd { - // It ends after the encoded segment so adjust the end to match - // the location before it was decoded - matchEndDelta := matchIndex[1] - s.decodedEnd - adjustedMatchIndex[1] = s.relativeEnd + matchEndDelta - } else { - // It ends within the encoded segment so set the bound to the relative end - adjustedMatchIndex[1] = s.relativeEnd - } - - // We're still not at a root segment so we'll need to keep on adjusting - if s.parent != nil { - return s.parent.adjustMatchIndex(adjustedMatchIndex) - } - - return adjustedMatchIndex -} - -// depth reports how many levels of decoding needed to be done (default is 1) -func (s EncodedSegment) depth() int { - depth := 1 - - // Climb the tree and increment the depth - for current := &s; current.parent != nil; current = current.parent { - depth++ - } - - return depth -} - -// tags returns additional meta data tags related to the types of segments -func (s EncodedSegment) tags() []string { - return []string{ - fmt.Sprintf("decoded:%s", s.encoding), - fmt.Sprintf("decode-depth:%d", s.depth()), - } -} - -// Decoder decodes various types of data in place -type Decoder struct { - decodedMap map[string]string -} - -// NewDecoder creates a default decoder struct -func NewDecoder() *Decoder { - return &Decoder{ - decodedMap: make(map[string]string), - } -} - -// decode returns the data with the values decoded in-place -func (d *Decoder) decode(data string, parentSegments []EncodedSegment) (string, []EncodedSegment) { - segments := d.findEncodedSegments(data, parentSegments) - - if len(segments) > 0 { - result := bytes.NewBuffer(make([]byte, 0, len(data))) - - relativeStart := 0 - for _, segment := range segments { - result.WriteString(data[relativeStart:segment.relativeStart]) - result.WriteString(segment.decodedValue) - relativeStart = segment.relativeEnd - } - result.WriteString(data[relativeStart:]) - - return result.String(), segments - } - - return data, segments -} - -// findEncodedSegments finds the encoded segments in the data and updates the -// segment tree for this pass -func (d *Decoder) findEncodedSegments(data string, parentSegments []EncodedSegment) []EncodedSegment { - if len(data) == 0 { - return []EncodedSegment{} - } - - matchIndices := b64Regexp.FindAllStringIndex(data, -1) - if matchIndices == nil { - return []EncodedSegment{} - } - - segments := make([]EncodedSegment, 0, len(matchIndices)) - - // Keeps up with offsets from the text changing size as things are decoded - decodedShift := 0 - - for _, matchIndex := range matchIndices { - encodedValue := data[matchIndex[0]:matchIndex[1]] - - if !isLikelyB64(encodedValue) { - d.decodedMap[encodedValue] = "" - continue - } - - decodedValue, alreadyDecoded := d.decodedMap[encodedValue] - - // We haven't decoded this yet, so go ahead and decode it - if !alreadyDecoded { - decodedValue = decodeValue(encodedValue) - d.decodedMap[encodedValue] = decodedValue - } - - // Skip this segment because there was nothing to check - if len(decodedValue) == 0 { - continue - } - - // Create a segment for the encoded data - segment := EncodedSegment{ - relativeStart: matchIndex[0], - relativeEnd: matchIndex[1], - absoluteStart: matchIndex[0], - absoluteEnd: matchIndex[1], - decodedStart: matchIndex[0] + decodedShift, - decodedEnd: matchIndex[0] + decodedShift + len(decodedValue), - decodedValue: decodedValue, - encoding: "base64", - } - - // Shift decoded start and ends based on size changes - decodedShift += len(decodedValue) - len(encodedValue) - - // Adjust the absolute position of segments contained in parent segments - for _, parentSegment := range parentSegments { - if segment.isChildOf(parentSegment) { - segment.absoluteStart = parentSegment.absoluteStart - segment.absoluteEnd = parentSegment.absoluteEnd - segment.parent = &parentSegment - break - } - } - - logging.Debug().Msgf("segment found: %#v", segment) - segments = append(segments, segment) - } - - return segments -} - -// decoders tries a list of decoders and returns the first successful one -func decodeValue(encodedValue string) string { - for _, decoder := range decoders { - decodedValue, err := decoder(encodedValue) - - if err == nil && len(decodedValue) > 0 && isASCII(decodedValue) { - return string(decodedValue) - } - } - - return "" -} - -func isASCII(b []byte) bool { - for i := 0; i < len(b); i++ { - if b[i] > unicode.MaxASCII || b[i] < '\t' { - return false - } - } - - return true -} - -// Skip a lot of method signatures and things at the risk of missing about -// 1% of base64 -func isLikelyB64(s string) bool { - for _, c := range s { - if b64LikelyChars[c] != 0 { - return true - } - } - - return false -} - -// Find a segment where the decoded bounds overlaps a range -func segmentWithDecodedOverlap(encodedSegments []EncodedSegment, start, end int) *EncodedSegment { - for _, segment := range encodedSegments { - if segment.decodedOverlaps(start, end) { - return &segment - } - } - - return nil -} - -func (s EncodedSegment) currentLine(currentRaw string) string { - start := 0 - end := len(currentRaw) - - // Find the start of the range - for i := s.decodedStart; i > -1; i-- { - c := currentRaw[i] - if c == '\n' { - start = i - break - } - } - - // Find the end of the range - for i := s.decodedEnd; i < end; i++ { - c := currentRaw[i] - if c == '\n' { - end = i - break - } - } - - return currentRaw[start:end] -} diff --git a/cli/detect/detect.go b/cli/detect/detect.go deleted file mode 100644 index f2e42cccc..000000000 --- a/cli/detect/detect.go +++ /dev/null @@ -1,699 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package detect - -import ( - "bufio" - "context" - "fmt" - "os" - "runtime" - "strings" - "sync" - "sync/atomic" - "time" - - "github.com/Infisical/infisical-merge/detect/config" - "github.com/Infisical/infisical-merge/detect/logging" - "github.com/Infisical/infisical-merge/detect/regexp" - "github.com/Infisical/infisical-merge/detect/report" - - ahocorasick "github.com/BobuSumisu/aho-corasick" - "github.com/fatih/semgroup" - "github.com/rs/zerolog" - "github.com/spf13/viper" - "golang.org/x/exp/maps" -) - -const ( - gitleaksAllowSignature = "gitleaks:allow" - chunkSize = 100 * 1_000 // 100kb - - // SlowWarningThreshold is the amount of time to wait before logging that a file is slow. - // This is useful for identifying problematic files and tuning the allowlist. - SlowWarningThreshold = 5 * time.Second -) - -var ( - newLineRegexp = regexp.MustCompile("\n") - isWindows = runtime.GOOS == "windows" -) - -// Detector is the main detector struct -type Detector struct { - // Config is the configuration for the detector - Config config.Config - - // Redact is a flag to redact findings. This is exported - // so users using gitleaks as a library can set this flag - // without calling `detector.Start(cmd *cobra.Command)` - Redact uint - - // verbose is a flag to print findings - Verbose bool - - // MaxDecodeDepths limits how many recursive decoding passes are allowed - MaxDecodeDepth int - - // files larger than this will be skipped - MaxTargetMegaBytes int - - // followSymlinks is a flag to enable scanning symlink files - FollowSymlinks bool - - // NoColor is a flag to disable color output - NoColor bool - - // IgnoreGitleaksAllow is a flag to ignore gitleaks:allow comments. - IgnoreGitleaksAllow bool - - // commitMap is used to keep track of commits that have been scanned. - // This is only used for logging purposes and git scans. - commitMap map[string]bool - - // findingMutex is to prevent concurrent access to the - // findings slice when adding findings. - findingMutex *sync.Mutex - - // findings is a slice of report.Findings. This is the result - // of the detector's scan which can then be used to generate a - // report. - findings []report.Finding - - // prefilter is a ahocorasick struct used for doing efficient string - // matching given a set of words (keywords from the rules in the config) - prefilter ahocorasick.Trie - - // a list of known findings that should be ignored - baseline []report.Finding - - // path to baseline - baselinePath string - - // gitleaksIgnore - gitleaksIgnore map[string]struct{} - - // Sema (https://github.com/fatih/semgroup) controls the concurrency - Sema *semgroup.Group - - // report-related settings. - ReportPath string - Reporter report.Reporter - - TotalBytes atomic.Uint64 -} - -// Fragment contains the data to be scanned -type Fragment struct { - // Raw is the raw content of the fragment - Raw string - - Bytes []byte - - // FilePath is the path to the file, if applicable. - // The path separator MUST be normalized to `/`. - FilePath string - SymlinkFile string - // WindowsFilePath is the path with the original separator. - // This provides a backwards-compatible solution to https://github.com/gitleaks/gitleaks/issues/1565. - WindowsFilePath string `json:"-"` // TODO: remove this in v9. - - // CommitSHA is the SHA of the commit if applicable - CommitSHA string - - // newlineIndices is a list of indices of newlines in the raw content. - // This is used to calculate the line location of a finding - newlineIndices [][]int -} - -// NewDetector creates a new detector with the given config -func NewDetector(cfg config.Config) *Detector { - return &Detector{ - commitMap: make(map[string]bool), - gitleaksIgnore: make(map[string]struct{}), - findingMutex: &sync.Mutex{}, - findings: make([]report.Finding, 0), - Config: cfg, - prefilter: *ahocorasick.NewTrieBuilder().AddStrings(maps.Keys(cfg.Keywords)).Build(), - Sema: semgroup.NewGroup(context.Background(), 40), - } -} - -// NewDetectorDefaultConfig creates a new detector with the default config -func NewDetectorDefaultConfig() (*Detector, error) { - viper.SetConfigType("toml") - err := viper.ReadConfig(strings.NewReader(config.DefaultConfig)) - if err != nil { - return nil, err - } - var vc config.ViperConfig - err = viper.Unmarshal(&vc) - if err != nil { - return nil, err - } - cfg, err := vc.Translate() - if err != nil { - return nil, err - } - return NewDetector(cfg), nil -} - -func (d *Detector) AddGitleaksIgnore(gitleaksIgnorePath string) error { - logging.Debug().Msgf("found .gitleaksignore file: %s", gitleaksIgnorePath) - file, err := os.Open(gitleaksIgnorePath) - if err != nil { - return err - } - defer func() { - // https://github.com/securego/gosec/issues/512 - if err := file.Close(); err != nil { - logging.Warn().Msgf("Error closing .gitleaksignore file: %s\n", err) - } - }() - - scanner := bufio.NewScanner(file) - replacer := strings.NewReplacer("\\", "/") - for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) - // Skip lines that start with a comment - if line == "" || strings.HasPrefix(line, "#") { - continue - } - - // Normalize the path. - // TODO: Make this a breaking change in v9. - s := strings.Split(line, ":") - switch len(s) { - case 3: - // Global fingerprint. - // `file:rule-id:start-line` - s[0] = replacer.Replace(s[0]) - case 4: - // Commit fingerprint. - // `commit:file:rule-id:start-line` - s[1] = replacer.Replace(s[1]) - default: - logging.Warn().Str("fingerprint", line).Msg("Invalid .gitleaksignore entry") - } - d.gitleaksIgnore[strings.Join(s, ":")] = struct{}{} - } - return nil -} - -// DetectBytes scans the given bytes and returns a list of findings -func (d *Detector) DetectBytes(content []byte) []report.Finding { - return d.DetectString(string(content)) -} - -// DetectString scans the given string and returns a list of findings -func (d *Detector) DetectString(content string) []report.Finding { - return d.Detect(Fragment{ - Raw: content, - }) -} - -// Detect scans the given fragment and returns a list of findings -func (d *Detector) Detect(fragment Fragment) []report.Finding { - if fragment.Bytes == nil { - d.TotalBytes.Add(uint64(len(fragment.Raw))) - } - d.TotalBytes.Add(uint64(len(fragment.Bytes))) - - var ( - findings []report.Finding - logger = func() zerolog.Logger { - l := logging.With().Str("path", fragment.FilePath) - if fragment.CommitSHA != "" { - l = l.Str("commit", fragment.CommitSHA) - } - return l.Logger() - }() - ) - - // check if filepath is allowed - if fragment.FilePath != "" { - // is the path our config or baseline file? - if fragment.FilePath == d.Config.Path || (d.baselinePath != "" && fragment.FilePath == d.baselinePath) { - logging.Trace().Msg("skipping file: matches config or baseline path") - return findings - } - } - // check if commit or filepath is allowed. - if isAllowed, event := checkCommitOrPathAllowed(logger, fragment, d.Config.Allowlists); isAllowed { - event.Msg("skipping file: global allowlist") - return findings - } - - // add newline indices for location calculation in detectRule - fragment.newlineIndices = newLineRegexp.FindAllStringIndex(fragment.Raw, -1) - - // setup variables to handle different decoding passes - currentRaw := fragment.Raw - encodedSegments := []EncodedSegment{} - currentDecodeDepth := 0 - decoder := NewDecoder() - - for { - // build keyword map for prefiltering rules - keywords := make(map[string]bool) - normalizedRaw := strings.ToLower(currentRaw) - matches := d.prefilter.MatchString(normalizedRaw) - for _, m := range matches { - keywords[normalizedRaw[m.Pos():int(m.Pos())+len(m.Match())]] = true - } - - for _, rule := range d.Config.Rules { - if len(rule.Keywords) == 0 { - // if no keywords are associated with the rule always scan the - // fragment using the rule - findings = append(findings, d.detectRule(fragment, currentRaw, rule, encodedSegments)...) - continue - } - - // check if keywords are in the fragment - for _, k := range rule.Keywords { - if _, ok := keywords[strings.ToLower(k)]; ok { - findings = append(findings, d.detectRule(fragment, currentRaw, rule, encodedSegments)...) - break - } - } - } - - // increment the depth by 1 as we start our decoding pass - currentDecodeDepth++ - - // stop the loop if we've hit our max decoding depth - if currentDecodeDepth > d.MaxDecodeDepth { - break - } - - // decode the currentRaw for the next pass - currentRaw, encodedSegments = decoder.decode(currentRaw, encodedSegments) - - // stop the loop when there's nothing else to decode - if len(encodedSegments) == 0 { - break - } - } - - return filter(findings, d.Redact) -} - -// detectRule scans the given fragment for the given rule and returns a list of findings -func (d *Detector) detectRule(fragment Fragment, currentRaw string, r config.Rule, encodedSegments []EncodedSegment) []report.Finding { - var ( - findings []report.Finding - logger = func() zerolog.Logger { - l := logging.With().Str("rule-id", r.RuleID).Str("path", fragment.FilePath) - if fragment.CommitSHA != "" { - l = l.Str("commit", fragment.CommitSHA) - } - return l.Logger() - }() - ) - - // check if commit or file is allowed for this rule. - if isAllowed, event := checkCommitOrPathAllowed(logger, fragment, r.Allowlists); isAllowed { - event.Msg("skipping file: rule allowlist") - return findings - } - - if r.Path != nil { - if r.Regex == nil && len(encodedSegments) == 0 { - // Path _only_ rule - if r.Path.MatchString(fragment.FilePath) || (fragment.WindowsFilePath != "" && r.Path.MatchString(fragment.WindowsFilePath)) { - finding := report.Finding{ - RuleID: r.RuleID, - Description: r.Description, - File: fragment.FilePath, - SymlinkFile: fragment.SymlinkFile, - Match: fmt.Sprintf("file detected: %s", fragment.FilePath), - Tags: r.Tags, - } - return append(findings, finding) - } - } else { - // if path is set _and_ a regex is set, then we need to check both - // so if the path does not match, then we should return early and not - // consider the regex - if !(r.Path.MatchString(fragment.FilePath) || (fragment.WindowsFilePath != "" && r.Path.MatchString(fragment.WindowsFilePath))) { - return findings - } - } - } - - // if path only rule, skip content checks - if r.Regex == nil { - return findings - } - - // if flag configure and raw data size bigger then the flag - if d.MaxTargetMegaBytes > 0 { - rawLength := len(currentRaw) / 1000000 - if rawLength > d.MaxTargetMegaBytes { - logger.Debug(). - Int("size", rawLength). - Int("max-size", d.MaxTargetMegaBytes). - Msg("skipping fragment: size") - return findings - } - } - - // use currentRaw instead of fragment.Raw since this represents the current - // decoding pass on the text - for _, matchIndex := range r.Regex.FindAllStringIndex(currentRaw, -1) { - // Extract secret from match - secret := strings.Trim(currentRaw[matchIndex[0]:matchIndex[1]], "\n") - - // For any meta data from decoding - var metaTags []string - currentLine := "" - - // Check if the decoded portions of the segment overlap with the match - // to see if its potentially a new match - if len(encodedSegments) > 0 { - if segment := segmentWithDecodedOverlap(encodedSegments, matchIndex[0], matchIndex[1]); segment != nil { - matchIndex = segment.adjustMatchIndex(matchIndex) - metaTags = append(metaTags, segment.tags()...) - currentLine = segment.currentLine(currentRaw) - } else { - // This item has already been added to a finding - continue - } - } else { - // Fixes: https://github.com/gitleaks/gitleaks/issues/1352 - // removes the incorrectly following line that was detected by regex expression '\n' - matchIndex[1] = matchIndex[0] + len(secret) - } - - // determine location of match. Note that the location - // in the finding will be the line/column numbers of the _match_ - // not the _secret_, which will be different if the secretGroup - // value is set for this rule - loc := location(fragment, matchIndex) - - if matchIndex[1] > loc.endLineIndex { - loc.endLineIndex = matchIndex[1] - } - - finding := report.Finding{ - RuleID: r.RuleID, - Description: r.Description, - StartLine: loc.startLine, - EndLine: loc.endLine, - StartColumn: loc.startColumn, - EndColumn: loc.endColumn, - Line: fragment.Raw[loc.startLineIndex:loc.endLineIndex], - Match: secret, - Secret: secret, - File: fragment.FilePath, - SymlinkFile: fragment.SymlinkFile, - Tags: append(r.Tags, metaTags...), - } - - if !d.IgnoreGitleaksAllow && strings.Contains(finding.Line, gitleaksAllowSignature) { - logger.Trace(). - Str("finding", finding.Secret). - Msg("skipping finding: 'gitleaks:allow' signature") - continue - } - - if currentLine == "" { - currentLine = finding.Line - } - - // Set the value of |secret|, if the pattern contains at least one capture group. - // (The first element is the full match, hence we check >= 2.) - groups := r.Regex.FindStringSubmatch(finding.Secret) - if len(groups) >= 2 { - if r.SecretGroup > 0 { - if len(groups) <= r.SecretGroup { - // Config validation should prevent this - continue - } - finding.Secret = groups[r.SecretGroup] - } else { - // If |secretGroup| is not set, we will use the first suitable capture group. - for _, s := range groups[1:] { - if len(s) > 0 { - finding.Secret = s - break - } - } - } - } - - // check entropy - entropy := shannonEntropy(finding.Secret) - finding.Entropy = float32(entropy) - if r.Entropy != 0.0 { - // entropy is too low, skip this finding - if entropy <= r.Entropy { - logger.Trace(). - Str("finding", finding.Secret). - Float32("entropy", finding.Entropy). - Msg("skipping finding: low entropy") - continue - } - } - - // check if the result matches any of the global allowlists. - if isAllowed, event := checkFindingAllowed(logger, finding, fragment, currentLine, d.Config.Allowlists); isAllowed { - event.Msg("skipping finding: global allowlist") - continue - } - - // check if the result matches any of the rule allowlists. - if isAllowed, event := checkFindingAllowed(logger, finding, fragment, currentLine, r.Allowlists); isAllowed { - event.Msg("skipping finding: rule allowlist") - continue - } - findings = append(findings, finding) - } - return findings -} - -// AddFinding synchronously adds a finding to the findings slice -func (d *Detector) AddFinding(finding report.Finding) { - globalFingerprint := fmt.Sprintf("%s:%s:%d", finding.File, finding.RuleID, finding.StartLine) - if finding.Commit != "" { - finding.Fingerprint = fmt.Sprintf("%s:%s:%s:%d", finding.Commit, finding.File, finding.RuleID, finding.StartLine) - } else { - finding.Fingerprint = globalFingerprint - } - - // check if we should ignore this finding - logger := logging.With().Str("finding", finding.Secret).Logger() - if _, ok := d.gitleaksIgnore[globalFingerprint]; ok { - logger.Debug(). - Str("fingerprint", globalFingerprint). - Msg("skipping finding: global fingerprint") - return - } else if finding.Commit != "" { - // Awkward nested if because I'm not sure how to chain these two conditions. - if _, ok := d.gitleaksIgnore[finding.Fingerprint]; ok { - logger.Debug(). - Str("fingerprint", finding.Fingerprint). - Msgf("skipping finding: fingerprint") - return - } - } - - if d.baseline != nil && !IsNew(finding, d.Redact, d.baseline) { - logger.Debug(). - Str("fingerprint", finding.Fingerprint). - Msgf("skipping finding: baseline") - return - } - - d.findingMutex.Lock() - d.findings = append(d.findings, finding) - if d.Verbose { - printFinding(finding, d.NoColor) - } - d.findingMutex.Unlock() -} - -// Findings returns the findings added to the detector -func (d *Detector) Findings() []report.Finding { - return d.findings -} - -// AddCommit synchronously adds a commit to the commit slice -func (d *Detector) addCommit(commit string) { - d.commitMap[commit] = true -} - -// checkCommitOrPathAllowed evaluates |fragment| against all provided |allowlists|. -// -// If the match condition is "OR", only commit and path are checked. -// Otherwise, if regexes or stopwords are defined this will fail. -func checkCommitOrPathAllowed( - logger zerolog.Logger, - fragment Fragment, - allowlists []*config.Allowlist, -) (bool, *zerolog.Event) { - if fragment.FilePath == "" && fragment.CommitSHA == "" { - return false, nil - } - - for _, a := range allowlists { - var ( - isAllowed bool - allowlistChecks []bool - commitAllowed, _ = a.CommitAllowed(fragment.CommitSHA) - pathAllowed = a.PathAllowed(fragment.FilePath) || (fragment.WindowsFilePath != "" && a.PathAllowed(fragment.WindowsFilePath)) - ) - // If the condition is "AND" we need to check all conditions. - if a.MatchCondition == config.AllowlistMatchAnd { - if len(a.Commits) > 0 { - allowlistChecks = append(allowlistChecks, commitAllowed) - } - if len(a.Paths) > 0 { - allowlistChecks = append(allowlistChecks, pathAllowed) - } - // These will be checked later. - if len(a.Regexes) > 0 { - continue - } - if len(a.StopWords) > 0 { - continue - } - - isAllowed = allTrue(allowlistChecks) - } else { - isAllowed = commitAllowed || pathAllowed - } - if isAllowed { - event := logger.Trace().Str("condition", a.MatchCondition.String()) - if commitAllowed { - event.Bool("allowed-commit", commitAllowed) - } - if pathAllowed { - event.Bool("allowed-path", pathAllowed) - } - return true, event - } - } - return false, nil -} - -// checkFindingAllowed evaluates |finding| against all provided |allowlists|. -// -// If the match condition is "OR", only regex and stopwords are run. (Commit and path should be handled separately). -// Otherwise, all conditions are checked. -// -// TODO: The method signature is awkward. I can't think of a better way to log helpful info. -func checkFindingAllowed( - logger zerolog.Logger, - finding report.Finding, - fragment Fragment, - currentLine string, - allowlists []*config.Allowlist, -) (bool, *zerolog.Event) { - for _, a := range allowlists { - allowlistTarget := finding.Secret - switch a.RegexTarget { - case "match": - allowlistTarget = finding.Match - case "line": - allowlistTarget = currentLine - } - - var ( - checks []bool - isAllowed bool - commitAllowed bool - commit string - pathAllowed bool - regexAllowed = a.RegexAllowed(allowlistTarget) - containsStopword, word = a.ContainsStopWord(finding.Secret) - ) - // If the condition is "AND" we need to check all conditions. - if a.MatchCondition == config.AllowlistMatchAnd { - // Determine applicable checks. - if len(a.Commits) > 0 { - commitAllowed, commit = a.CommitAllowed(fragment.CommitSHA) - checks = append(checks, commitAllowed) - } - if len(a.Paths) > 0 { - pathAllowed = a.PathAllowed(fragment.FilePath) || (fragment.WindowsFilePath != "" && a.PathAllowed(fragment.WindowsFilePath)) - checks = append(checks, pathAllowed) - } - if len(a.Regexes) > 0 { - checks = append(checks, regexAllowed) - } - if len(a.StopWords) > 0 { - checks = append(checks, containsStopword) - } - - isAllowed = allTrue(checks) - } else { - isAllowed = regexAllowed || containsStopword - } - - if isAllowed { - event := logger.Trace(). - Str("finding", finding.Secret). - Str("condition", a.MatchCondition.String()) - if commitAllowed { - event.Str("allowed-commit", commit) - } - if pathAllowed { - event.Bool("allowed-path", pathAllowed) - } - if regexAllowed { - event.Bool("allowed-regex", regexAllowed) - } - if containsStopword { - event.Str("allowed-stopword", word) - } - return true, event - } - } - return false, nil -} - -func allTrue(bools []bool) bool { - for _, check := range bools { - if !check { - return false - } - } - return true -} - -func fileExists(fileName string) bool { - // check for a .infisicalignore file - info, err := os.Stat(fileName) - if err != nil && !os.IsNotExist(err) { - return false - } - - if info != nil && err == nil { - if !info.IsDir() { - return true - } - } - return false -} diff --git a/cli/detect/directory.go b/cli/detect/directory.go deleted file mode 100644 index 56f4999f2..000000000 --- a/cli/detect/directory.go +++ /dev/null @@ -1,225 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package detect - -import ( - "bufio" - "bytes" - "io" - "os" - "path/filepath" - "strings" - "time" - - "github.com/h2non/filetype" - - "github.com/Infisical/infisical-merge/detect/logging" - "github.com/Infisical/infisical-merge/detect/report" - "github.com/Infisical/infisical-merge/detect/sources" -) - -const maxPeekSize = 25 * 1_000 // 10kb - -func (d *Detector) DetectFiles(paths <-chan sources.ScanTarget) ([]report.Finding, error) { - for pa := range paths { - d.Sema.Go(func() error { - logger := logging.With().Str("path", pa.Path).Logger() - logger.Trace().Msg("Scanning path") - - f, err := os.Open(pa.Path) - if err != nil { - if os.IsPermission(err) { - logger.Warn().Msg("Skipping file: permission denied") - return nil - } - return err - } - defer func() { - _ = f.Close() - }() - - // Get file size - fileInfo, err := f.Stat() - if err != nil { - return err - } - fileSize := fileInfo.Size() - if d.MaxTargetMegaBytes > 0 { - rawLength := fileSize / 1000000 - if rawLength > int64(d.MaxTargetMegaBytes) { - logger.Debug(). - Int64("size", rawLength). - Msg("Skipping file: exceeds --max-target-megabytes") - return nil - } - } - - var ( - // Buffer to hold file chunks - reader = bufio.NewReaderSize(f, chunkSize) - buf = make([]byte, chunkSize) - totalLines = 0 - ) - for { - n, err := reader.Read(buf) - - // "Callers should always process the n > 0 bytes returned before considering the error err." - // https://pkg.go.dev/io#Reader - if n > 0 { - // Only check the filetype at the start of file. - if totalLines == 0 { - // TODO: could other optimizations be introduced here? - if mimetype, err := filetype.Match(buf[:n]); err != nil { - return nil - } else if mimetype.MIME.Type == "application" { - return nil // skip binary files - } - } - - // Try to split chunks across large areas of whitespace, if possible. - peekBuf := bytes.NewBuffer(buf[:n]) - if readErr := readUntilSafeBoundary(reader, n, maxPeekSize, peekBuf); readErr != nil { - return readErr - } - - // Count the number of newlines in this chunk - chunk := peekBuf.String() - linesInChunk := strings.Count(chunk, "\n") - totalLines += linesInChunk - fragment := Fragment{ - Raw: chunk, - Bytes: peekBuf.Bytes(), - } - if pa.Symlink != "" { - fragment.SymlinkFile = pa.Symlink - } - - if isWindows { - fragment.FilePath = filepath.ToSlash(pa.Path) - fragment.SymlinkFile = filepath.ToSlash(fragment.SymlinkFile) - fragment.WindowsFilePath = pa.Path - } else { - fragment.FilePath = pa.Path - } - - timer := time.AfterFunc(SlowWarningThreshold, func() { - logger.Debug().Msgf("Taking longer than %s to inspect fragment", SlowWarningThreshold.String()) - }) - for _, finding := range d.Detect(fragment) { - // need to add 1 since line counting starts at 1 - finding.StartLine += (totalLines - linesInChunk) + 1 - finding.EndLine += (totalLines - linesInChunk) + 1 - d.AddFinding(finding) - } - if timer != nil { - timer.Stop() - timer = nil - } - } - - if err != nil { - if err == io.EOF { - return nil - } - return err - } - } - }) - } - - if err := d.Sema.Wait(); err != nil { - return d.findings, err - } - - return d.findings, nil -} - -// readUntilSafeBoundary consumes |f| until it finds two consecutive `\n` characters, up to |maxPeekSize|. -// This hopefully avoids splitting. (https://github.com/gitleaks/gitleaks/issues/1651) -func readUntilSafeBoundary(r *bufio.Reader, n int, maxPeekSize int, peekBuf *bytes.Buffer) error { - if peekBuf.Len() == 0 { - return nil - } - - // Does the buffer end in consecutive newlines? - var ( - data = peekBuf.Bytes() - lastChar = data[len(data)-1] - newlineCount = 0 // Tracks consecutive newlines - ) - if isWhitespace(lastChar) { - for i := len(data) - 1; i >= 0; i-- { - lastChar = data[i] - if lastChar == '\n' { - newlineCount++ - - // Stop if two consecutive newlines are found - if newlineCount >= 2 { - return nil - } - } else if lastChar == '\r' || lastChar == ' ' || lastChar == '\t' { - // The presence of other whitespace characters (`\r`, ` `, `\t`) shouldn't reset the count. - // (Intentionally do nothing.) - } else { - break - } - } - } - - // If not, read ahead until we (hopefully) find some. - newlineCount = 0 - for { - data = peekBuf.Bytes() - // Check if the last character is a newline. - lastChar = data[len(data)-1] - if lastChar == '\n' { - newlineCount++ - - // Stop if two consecutive newlines are found - if newlineCount >= 2 { - break - } - } else if lastChar == '\r' || lastChar == ' ' || lastChar == '\t' { - // The presence of other whitespace characters (`\r`, ` `, `\t`) shouldn't reset the count. - // (Intentionally do nothing.) - } else { - newlineCount = 0 // Reset if a non-newline character is found - } - - // Stop growing the buffer if it reaches maxSize - if (peekBuf.Len() - n) >= maxPeekSize { - break - } - - // Read additional data into a temporary buffer - b, err := r.ReadByte() - if err != nil { - if err == io.EOF { - break - } - return err - } - peekBuf.WriteByte(b) - } - return nil -} diff --git a/cli/detect/git.go b/cli/detect/git.go deleted file mode 100644 index 83ed8a853..000000000 --- a/cli/detect/git.go +++ /dev/null @@ -1,216 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package detect - -import ( - "bytes" - "errors" - "fmt" - "net/url" - "os/exec" - "regexp" - "strings" - "time" - - "github.com/Infisical/infisical-merge/detect/cmd/scm" - "github.com/gitleaks/go-gitdiff/gitdiff" - - "github.com/Infisical/infisical-merge/detect/logging" - "github.com/Infisical/infisical-merge/detect/report" - "github.com/Infisical/infisical-merge/detect/sources" -) - -func (d *Detector) DetectGit(cmd *sources.GitCmd, remote *RemoteInfo) ([]report.Finding, error) { - defer cmd.Wait() - var ( - diffFilesCh = cmd.DiffFilesCh() - errCh = cmd.ErrCh() - ) - - // loop to range over both DiffFiles (stdout) and ErrCh (stderr) - for diffFilesCh != nil || errCh != nil { - select { - case gitdiffFile, open := <-diffFilesCh: - if !open { - diffFilesCh = nil - break - } - - // skip binary files - if gitdiffFile.IsBinary || gitdiffFile.IsDelete { - continue - } - - // Check if commit is allowed - commitSHA := "" - if gitdiffFile.PatchHeader != nil { - commitSHA = gitdiffFile.PatchHeader.SHA - for _, a := range d.Config.Allowlists { - if ok, c := a.CommitAllowed(gitdiffFile.PatchHeader.SHA); ok { - logging.Trace().Str("allowed-commit", c).Msg("skipping commit: global allowlist") - continue - } - } - } - d.addCommit(commitSHA) - - d.Sema.Go(func() error { - for _, textFragment := range gitdiffFile.TextFragments { - if textFragment == nil { - return nil - } - - fragment := Fragment{ - Raw: textFragment.Raw(gitdiff.OpAdd), - CommitSHA: commitSHA, - FilePath: gitdiffFile.NewName, - } - - timer := time.AfterFunc(SlowWarningThreshold, func() { - logging.Debug(). - Str("commit", commitSHA[:7]). - Str("path", fragment.FilePath). - Msgf("Taking longer than %s to inspect fragment", SlowWarningThreshold.String()) - }) - for _, finding := range d.Detect(fragment) { - d.AddFinding(augmentGitFinding(remote, finding, textFragment, gitdiffFile)) - } - if timer != nil { - timer.Stop() - timer = nil - } - } - return nil - }) - case err, open := <-errCh: - if !open { - errCh = nil - break - } - - return d.findings, err - } - } - - if err := d.Sema.Wait(); err != nil { - return d.findings, err - } - logging.Info().Msgf("%d commits scanned.", len(d.commitMap)) - logging.Debug().Msg("Note: this number might be smaller than expected due to commits with no additions") - return d.findings, nil -} - -type RemoteInfo struct { - Platform scm.Platform - Url string -} - -func NewRemoteInfo(platform scm.Platform, source string) *RemoteInfo { - if platform == scm.NoPlatform { - return &RemoteInfo{Platform: platform} - } - - remoteUrl, err := getRemoteUrl(source) - if err != nil { - if strings.Contains(err.Error(), "No remote configured") { - logging.Debug().Msg("skipping finding links: repository has no configured remote.") - platform = scm.NoPlatform - } else { - logging.Error().Err(err).Msg("skipping finding links: unable to parse remote URL") - } - goto End - } - - if platform == scm.UnknownPlatform { - platform = platformFromHost(remoteUrl) - if platform == scm.UnknownPlatform { - logging.Info(). - Str("host", remoteUrl.Hostname()). - Msg("Unknown SCM platform. Use --platform to include links in findings.") - } else { - logging.Debug(). - Str("host", remoteUrl.Hostname()). - Str("platform", platform.String()). - Msg("SCM platform parsed from host") - } - } - -End: - var rUrl string - if remoteUrl != nil { - rUrl = remoteUrl.String() - } - return &RemoteInfo{ - Platform: platform, - Url: rUrl, - } -} - -var sshUrlpat = regexp.MustCompile(`^git@([a-zA-Z0-9.-]+):([\w/.-]+?)(?:\.git)?$`) - -func getRemoteUrl(source string) (*url.URL, error) { - // This will return the first remote — typically, "origin". - cmd := exec.Command("git", "ls-remote", "--quiet", "--get-url") - if source != "." { - cmd.Dir = source - } - - stdout, err := cmd.Output() - if err != nil { - var exitError *exec.ExitError - if errors.As(err, &exitError) { - return nil, fmt.Errorf("command failed (%d): %w, stderr: %s", exitError.ExitCode(), err, string(bytes.TrimSpace(exitError.Stderr))) - } - return nil, err - } - - remoteUrl := string(bytes.TrimSpace(stdout)) - if matches := sshUrlpat.FindStringSubmatch(remoteUrl); matches != nil { - remoteUrl = fmt.Sprintf("https://%s/%s", matches[1], matches[2]) - } - remoteUrl = strings.TrimSuffix(remoteUrl, ".git") - - parsedUrl, err := url.Parse(remoteUrl) - if err != nil { - return nil, fmt.Errorf("unable to parse remote URL: %w", err) - } - - // Remove any user info. - parsedUrl.User = nil - return parsedUrl, nil -} - -func platformFromHost(u *url.URL) scm.Platform { - switch strings.ToLower(u.Hostname()) { - case "github.com": - return scm.GitHubPlatform - case "gitlab.com": - return scm.GitLabPlatform - case "dev.azure.com", "visualstudio.com": - return scm.AzureDevOpsPlatform - case "bitbucket.org": - return scm.BitBucketPlatform - default: - return scm.UnknownPlatform - } -} diff --git a/cli/detect/location.go b/cli/detect/location.go deleted file mode 100644 index 81419511c..000000000 --- a/cli/detect/location.go +++ /dev/null @@ -1,102 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package detect - -// Location represents a location in a file -type Location struct { - startLine int - endLine int - startColumn int - endColumn int - startLineIndex int - endLineIndex int -} - -func location(fragment Fragment, matchIndex []int) Location { - var ( - prevNewLine int - location Location - lineSet bool - _lineNum int - ) - - start := matchIndex[0] - end := matchIndex[1] - - // default startLineIndex to 0 - location.startLineIndex = 0 - - // Fixes: https://github.com/zricethezav/gitleaks/issues/1037 - // When a fragment does NOT have any newlines, a default "newline" - // will be counted to make the subsequent location calculation logic work - // for fragments will no newlines. - if len(fragment.newlineIndices) == 0 { - fragment.newlineIndices = [][]int{ - {len(fragment.Raw), len(fragment.Raw) + 1}, - } - } - - for lineNum, pair := range fragment.newlineIndices { - _lineNum = lineNum - newLineByteIndex := pair[0] - if prevNewLine <= start && start < newLineByteIndex { - lineSet = true - location.startLine = lineNum - location.endLine = lineNum - location.startColumn = (start - prevNewLine) + 1 // +1 because counting starts at 1 - location.startLineIndex = prevNewLine - location.endLineIndex = newLineByteIndex - } - if prevNewLine < end && end <= newLineByteIndex { - location.endLine = lineNum - location.endColumn = (end - prevNewLine) - location.endLineIndex = newLineByteIndex - } - - prevNewLine = pair[0] - } - - if !lineSet { - // if lines never get set then that means the secret is most likely - // on the last line of the diff output and the diff output does not have - // a newline - location.startColumn = (start - prevNewLine) + 1 // +1 because counting starts at 1 - location.endColumn = (end - prevNewLine) - location.startLine = _lineNum + 1 - location.endLine = _lineNum + 1 - - // search for new line byte index - i := 0 - for end+i < len(fragment.Raw) { - if fragment.Raw[end+i] == '\n' { - break - } - if fragment.Raw[end+i] == '\r' { - break - } - i++ - } - location.endLineIndex = end + i - } - return location -} diff --git a/cli/detect/logging/log.go b/cli/detect/logging/log.go deleted file mode 100644 index efac01725..000000000 --- a/cli/detect/logging/log.go +++ /dev/null @@ -1,72 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package logging - -import ( - "os" - - "github.com/rs/zerolog" -) - -var Logger zerolog.Logger - -func init() { - // send all logs to stdout - Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}). - Level(zerolog.InfoLevel). - With().Timestamp().Logger() -} - -func With() zerolog.Context { - return Logger.With() -} - -func Trace() *zerolog.Event { - return Logger.Trace() -} - -func Debug() *zerolog.Event { - return Logger.Debug() -} -func Info() *zerolog.Event { - return Logger.Info() -} -func Warn() *zerolog.Event { - return Logger.Warn() -} - -func Error() *zerolog.Event { - return Logger.Error() -} - -func Err(err error) *zerolog.Event { - return Logger.Err(err) -} - -func Fatal() *zerolog.Event { - return Logger.Fatal() -} - -func Panic() *zerolog.Event { - return Logger.Panic() -} diff --git a/cli/detect/reader.go b/cli/detect/reader.go deleted file mode 100644 index d3559b68a..000000000 --- a/cli/detect/reader.go +++ /dev/null @@ -1,149 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package detect - -import ( - "bufio" - "bytes" - "errors" - "io" - - "github.com/Infisical/infisical-merge/detect/report" -) - -// DetectReader accepts an io.Reader and a buffer size for the reader in KB -func (d *Detector) DetectReader(r io.Reader, bufSize int) ([]report.Finding, error) { - reader := bufio.NewReader(r) - buf := make([]byte, 1000*bufSize) - findings := []report.Finding{} - - for { - n, err := reader.Read(buf) - - // "Callers should always process the n > 0 bytes returned before considering the error err." - // https://pkg.go.dev/io#Reader - if n > 0 { - // Try to split chunks across large areas of whitespace, if possible. - peekBuf := bytes.NewBuffer(buf[:n]) - if readErr := readUntilSafeBoundary(reader, n, maxPeekSize, peekBuf); readErr != nil { - return findings, readErr - } - - fragment := Fragment{ - Raw: peekBuf.String(), - } - for _, finding := range d.Detect(fragment) { - findings = append(findings, finding) - if d.Verbose { - printFinding(finding, d.NoColor) - } - } - } - - if err != nil { - if err == io.EOF { - break - } - return findings, err - } - } - - return findings, nil -} - -// StreamDetectReader streams the detection results from the provided io.Reader. -// It reads data using the specified buffer size (in KB) and processes each chunk through -// the existing detection logic. Findings are sent down the returned findings channel as soon as -// they are detected, while a separate error channel signals a terminal error (or nil upon successful completion). -// The function returns two channels: -// - findingsCh: a receive-only channel that emits report.Finding objects as they are found. -// - errCh: a receive-only channel that emits a single final error (or nil if no error occurred) -// once the stream ends. -// -// Recommended Usage: -// -// Since there will only ever be a single value on the errCh, it is recommended to consume the findingsCh -// first. Once findingsCh is closed, the consumer should then read from errCh to determine -// if the stream completed successfully or if an error occurred. -// -// This design avoids the need for a select loop, keeping client code simple. -// -// Example: -// -// // Assume detector is an instance of *Detector and myReader implements io.Reader. -// findingsCh, errCh := detector.StreamDetectReader(myReader, 64) // using 64 KB buffer size -// -// // Process findings as they arrive. -// for finding := range findingsCh { -// fmt.Printf("Found secret: %+v\n", finding) -// } -// -// // After the findings channel is closed, check the final error. -// if err := <-errCh; err != nil { -// log.Fatalf("StreamDetectReader encountered an error: %v", err) -// } else { -// fmt.Println("Scanning completed successfully.") -// } -func (d *Detector) StreamDetectReader(r io.Reader, bufSize int) (<-chan report.Finding, <-chan error) { - findingsCh := make(chan report.Finding, 1) - errCh := make(chan error, 1) - - go func() { - defer close(findingsCh) - defer close(errCh) - - reader := bufio.NewReader(r) - buf := make([]byte, 1000*bufSize) - - for { - n, err := reader.Read(buf) - - if n > 0 { - peekBuf := bytes.NewBuffer(buf[:n]) - if readErr := readUntilSafeBoundary(reader, n, maxPeekSize, peekBuf); readErr != nil { - errCh <- readErr - return - } - - fragment := Fragment{Raw: peekBuf.String()} - for _, finding := range d.Detect(fragment) { - findingsCh <- finding - if d.Verbose { - printFinding(finding, d.NoColor) - } - } - } - - if err != nil { - if errors.Is(err, io.EOF) { - errCh <- nil - return - } - errCh <- err - return - } - } - }() - - return findingsCh, errCh -} diff --git a/cli/detect/regexp/stdlib_regex.go b/cli/detect/regexp/stdlib_regex.go deleted file mode 100644 index 81e2089b7..000000000 --- a/cli/detect/regexp/stdlib_regex.go +++ /dev/null @@ -1,37 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -//go:build !gore2regex - -package regexp - -import ( - re "regexp" -) - -const Version = "stdlib" - -type Regexp = re.Regexp - -func MustCompile(str string) *re.Regexp { - return re.MustCompile(str) -} diff --git a/cli/detect/regexp/wasilibs_regex.go b/cli/detect/regexp/wasilibs_regex.go deleted file mode 100644 index bc64fb14b..000000000 --- a/cli/detect/regexp/wasilibs_regex.go +++ /dev/null @@ -1,37 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -//go:build gore2regex - -package regexp - -import ( - re "github.com/wasilibs/go-re2" -) - -const Version = "github.com/wasilibs/go-re2" - -type Regexp = re.Regexp - -func MustCompile(str string) *re.Regexp { - return re.MustCompile(str) -} diff --git a/cli/detect/report/constants.go b/cli/detect/report/constants.go deleted file mode 100644 index c4f06a9a3..000000000 --- a/cli/detect/report/constants.go +++ /dev/null @@ -1,26 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package report - -const version = "v8.0.0" -const driver = "gitleaks" diff --git a/cli/detect/report/csv.go b/cli/detect/report/csv.go deleted file mode 100644 index 1f8812f97..000000000 --- a/cli/detect/report/csv.go +++ /dev/null @@ -1,100 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package report - -import ( - "encoding/csv" - "io" - "strconv" - "strings" -) - -type CsvReporter struct { -} - -var _ Reporter = (*CsvReporter)(nil) - -func (r *CsvReporter) Write(w io.WriteCloser, findings []Finding) error { - if len(findings) == 0 { - return nil - } - - var ( - cw = csv.NewWriter(w) - err error - ) - columns := []string{"RuleID", - "Commit", - "File", - "SymlinkFile", - "Secret", - "Match", - "StartLine", - "EndLine", - "StartColumn", - "EndColumn", - "Author", - "Message", - "Date", - "Email", - "Fingerprint", - "Tags", - } - // A miserable attempt at "omitempty" so tests don't yell at me. - if findings[0].Link != "" { - columns = append(columns, "Link") - } - - if err = cw.Write(columns); err != nil { - return err - } - for _, f := range findings { - row := []string{f.RuleID, - f.Commit, - f.File, - f.SymlinkFile, - f.Secret, - f.Match, - strconv.Itoa(f.StartLine), - strconv.Itoa(f.EndLine), - strconv.Itoa(f.StartColumn), - strconv.Itoa(f.EndColumn), - f.Author, - f.Message, - f.Date, - f.Email, - f.Fingerprint, - strings.Join(f.Tags, " "), - } - if findings[0].Link != "" { - row = append(row, f.Link) - } - - if err = cw.Write(row); err != nil { - return err - } - } - - cw.Flush() - return cw.Error() -} diff --git a/cli/detect/report/finding.go b/cli/detect/report/finding.go deleted file mode 100644 index c53f16ee7..000000000 --- a/cli/detect/report/finding.go +++ /dev/null @@ -1,92 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package report - -import ( - "math" - "strings" -) - -// Finding contains information about strings that -// have been captured by a tree-sitter query. -type Finding struct { - // Rule is the name of the rule that was matched - RuleID string - Description string - - StartLine int - EndLine int - StartColumn int - EndColumn int - - Line string `json:"-"` - - Match string - - // Secret contains the full content of what is matched in - // the tree-sitter query. - Secret string - - // File is the name of the file containing the finding - File string - SymlinkFile string - Commit string - Link string `json:",omitempty"` - - // Entropy is the shannon entropy of Value - Entropy float32 - - Author string - Email string - Date string - Message string - Tags []string - - // unique identifier - Fingerprint string -} - -// Redact removes sensitive information from a finding. -func (f *Finding) Redact(percent uint) { - secret := maskSecret(f.Secret, percent) - if percent >= 100 { - secret = "REDACTED" - } - f.Line = strings.Replace(f.Line, f.Secret, secret, -1) - f.Match = strings.Replace(f.Match, f.Secret, secret, -1) - f.Secret = secret -} - -func maskSecret(secret string, percent uint) string { - if percent > 100 { - percent = 100 - } - len := float64(len(secret)) - if len <= 0 { - return secret - } - prc := float64(100 - percent) - lth := int64(math.RoundToEven(len * prc / float64(100))) - - return secret[:lth] + "..." -} diff --git a/cli/detect/report/json.go b/cli/detect/report/json.go deleted file mode 100644 index f47b7eee0..000000000 --- a/cli/detect/report/json.go +++ /dev/null @@ -1,39 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package report - -import ( - "encoding/json" - "io" -) - -type JsonReporter struct { -} - -var _ Reporter = (*JsonReporter)(nil) - -func (t *JsonReporter) Write(w io.WriteCloser, findings []Finding) error { - encoder := json.NewEncoder(w) - encoder.SetIndent("", " ") - return encoder.Encode(findings) -} diff --git a/cli/detect/report/junit.go b/cli/detect/report/junit.go deleted file mode 100644 index 0862a45f1..000000000 --- a/cli/detect/report/junit.go +++ /dev/null @@ -1,129 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package report - -import ( - "encoding/json" - "encoding/xml" - "fmt" - "io" - "strconv" -) - -type JunitReporter struct { -} - -var _ Reporter = (*JunitReporter)(nil) - -func (r *JunitReporter) Write(w io.WriteCloser, findings []Finding) error { - testSuites := TestSuites{ - TestSuites: getTestSuites(findings), - } - - io.WriteString(w, xml.Header) - encoder := xml.NewEncoder(w) - encoder.Indent("", "\t") - return encoder.Encode(testSuites) -} - -func getTestSuites(findings []Finding) []TestSuite { - return []TestSuite{ - { - Failures: strconv.Itoa(len(findings)), - Name: "gitleaks", - Tests: strconv.Itoa(len(findings)), - TestCases: getTestCases(findings), - Time: "", - }, - } -} - -func getTestCases(findings []Finding) []TestCase { - testCases := []TestCase{} - for _, f := range findings { - testCase := TestCase{ - Classname: f.Description, - Failure: getFailure(f), - File: f.File, - Name: getMessage(f), - Time: "", - } - testCases = append(testCases, testCase) - } - return testCases -} - -func getFailure(f Finding) Failure { - return Failure{ - Data: getData(f), - Message: getMessage(f), - Type: f.Description, - } -} - -func getData(f Finding) string { - data, err := json.MarshalIndent(f, "", "\t") - if err != nil { - fmt.Println(err) - return "" - } - return string(data) -} - -func getMessage(f Finding) string { - if f.Commit == "" { - return fmt.Sprintf("%s has detected a secret in file %s, line %s.", f.RuleID, f.File, strconv.Itoa(f.StartLine)) - } - - return fmt.Sprintf("%s has detected a secret in file %s, line %s, at commit %s.", f.RuleID, f.File, strconv.Itoa(f.StartLine), f.Commit) -} - -type TestSuites struct { - XMLName xml.Name `xml:"testsuites"` - TestSuites []TestSuite -} - -type TestSuite struct { - XMLName xml.Name `xml:"testsuite"` - Failures string `xml:"failures,attr"` - Name string `xml:"name,attr"` - Tests string `xml:"tests,attr"` - TestCases []TestCase `xml:"testcase"` - Time string `xml:"time,attr"` -} - -type TestCase struct { - XMLName xml.Name `xml:"testcase"` - Classname string `xml:"classname,attr"` - Failure Failure `xml:"failure"` - File string `xml:"file,attr"` - Name string `xml:"name,attr"` - Time string `xml:"time,attr"` -} - -type Failure struct { - XMLName xml.Name `xml:"failure"` - Data string `xml:",chardata"` - Message string `xml:"message,attr"` - Type string `xml:"type,attr"` -} diff --git a/cli/detect/report/report.go b/cli/detect/report/report.go deleted file mode 100644 index 120841bb8..000000000 --- a/cli/detect/report/report.go +++ /dev/null @@ -1,38 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package report - -import ( - "io" -) - -const ( - // https://cwe.mitre.org/data/definitions/798.html - CWE = "CWE-798" - CWE_DESCRIPTION = "Use of Hard-coded Credentials" - StdoutReportPath = "-" -) - -type Reporter interface { - Write(w io.WriteCloser, findings []Finding) error -} diff --git a/cli/detect/report/sarif.go b/cli/detect/report/sarif.go deleted file mode 100644 index f7457eb57..000000000 --- a/cli/detect/report/sarif.go +++ /dev/null @@ -1,239 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package report - -import ( - "encoding/json" - "fmt" - "io" - - "github.com/Infisical/infisical-merge/detect/config" -) - -type SarifReporter struct { - OrderedRules []config.Rule -} - -var _ Reporter = (*SarifReporter)(nil) - -func (r *SarifReporter) Write(w io.WriteCloser, findings []Finding) error { - sarif := Sarif{ - Schema: "https://json.schemastore.org/sarif-2.1.0.json", - Version: "2.1.0", - Runs: r.getRuns(findings), - } - - encoder := json.NewEncoder(w) - encoder.SetIndent("", " ") - return encoder.Encode(sarif) -} - -func (r *SarifReporter) getRuns(findings []Finding) []Runs { - return []Runs{ - { - Tool: r.getTool(), - Results: getResults(findings), - }, - } -} - -func (r *SarifReporter) getTool() Tool { - tool := Tool{ - Driver: Driver{ - Name: driver, - SemanticVersion: version, - InformationUri: "https://github.com/gitleaks/gitleaks", - Rules: r.getRules(), - }, - } - - // if this tool has no rules, ensure that it is represented as [] instead of null/nil - if hasEmptyRules(tool) { - tool.Driver.Rules = make([]Rules, 0) - } - - return tool -} - -func hasEmptyRules(tool Tool) bool { - return len(tool.Driver.Rules) == 0 -} - -func (r *SarifReporter) getRules() []Rules { - // TODO for _, rule := range cfg.Rules { - var rules []Rules - for _, rule := range r.OrderedRules { - rules = append(rules, Rules{ - ID: rule.RuleID, - Description: ShortDescription{ - Text: rule.Description, - }, - }) - } - return rules -} - -func messageText(f Finding) string { - if f.Commit == "" { - return fmt.Sprintf("%s has detected secret for file %s.", f.RuleID, f.File) - } - - return fmt.Sprintf("%s has detected secret for file %s at commit %s.", f.RuleID, f.File, f.Commit) - -} - -func getResults(findings []Finding) []Results { - results := []Results{} - for _, f := range findings { - r := Results{ - Message: Message{ - Text: messageText(f), - }, - RuleId: f.RuleID, - Locations: getLocation(f), - // This information goes in partial fingerprings until revision - // data can be added somewhere else - PartialFingerPrints: PartialFingerPrints{ - CommitSha: f.Commit, - Email: f.Email, - CommitMessage: f.Message, - Date: f.Date, - Author: f.Author, - }, - Properties: Properties{ - Tags: f.Tags, - }, - } - results = append(results, r) - } - return results -} - -func getLocation(f Finding) []Locations { - uri := f.File - if f.SymlinkFile != "" { - uri = f.SymlinkFile - } - return []Locations{ - { - PhysicalLocation: PhysicalLocation{ - ArtifactLocation: ArtifactLocation{ - URI: uri, - }, - Region: Region{ - StartLine: f.StartLine, - EndLine: f.EndLine, - StartColumn: f.StartColumn, - EndColumn: f.EndColumn, - Snippet: Snippet{ - Text: f.Secret, - }, - }, - }, - }, - } -} - -type PartialFingerPrints struct { - CommitSha string `json:"commitSha"` - Email string `json:"email"` - Author string `json:"author"` - Date string `json:"date"` - CommitMessage string `json:"commitMessage"` -} - -type Sarif struct { - Schema string `json:"$schema"` - Version string `json:"version"` - Runs []Runs `json:"runs"` -} - -type ShortDescription struct { - Text string `json:"text"` -} - -type FullDescription struct { - Text string `json:"text"` -} - -type Rules struct { - ID string `json:"id"` - Description ShortDescription `json:"shortDescription"` -} - -type Driver struct { - Name string `json:"name"` - SemanticVersion string `json:"semanticVersion"` - InformationUri string `json:"informationUri"` - Rules []Rules `json:"rules"` -} - -type Tool struct { - Driver Driver `json:"driver"` -} - -type Message struct { - Text string `json:"text"` -} - -type ArtifactLocation struct { - URI string `json:"uri"` -} - -type Region struct { - StartLine int `json:"startLine"` - StartColumn int `json:"startColumn"` - EndLine int `json:"endLine"` - EndColumn int `json:"endColumn"` - Snippet Snippet `json:"snippet"` -} - -type Snippet struct { - Text string `json:"text"` -} - -type PhysicalLocation struct { - ArtifactLocation ArtifactLocation `json:"artifactLocation"` - Region Region `json:"region"` -} - -type Locations struct { - PhysicalLocation PhysicalLocation `json:"physicalLocation"` -} - -type Properties struct { - Tags []string `json:"tags"` -} - -type Results struct { - Message Message `json:"message"` - RuleId string `json:"ruleId"` - Locations []Locations `json:"locations"` - PartialFingerPrints `json:"partialFingerprints"` - Properties Properties `json:"properties"` -} - -type Runs struct { - Tool Tool `json:"tool"` - Results []Results `json:"results"` -} diff --git a/cli/detect/report/template.go b/cli/detect/report/template.go deleted file mode 100644 index 094aaaea9..000000000 --- a/cli/detect/report/template.go +++ /dev/null @@ -1,68 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package report - -import ( - "fmt" - "io" - "os" - "text/template" - - "github.com/Masterminds/sprig/v3" -) - -type TemplateReporter struct { - template *template.Template -} - -var _ Reporter = (*TemplateReporter)(nil) - -func NewTemplateReporter(templatePath string) (*TemplateReporter, error) { - if templatePath == "" { - return nil, fmt.Errorf("template path cannot be empty") - } - - file, err := os.ReadFile(templatePath) - if err != nil { - return nil, fmt.Errorf("error reading file: %w", err) - } - templateText := string(file) - - // TODO: Add helper functions like escaping for JSON, XML, etc. - t := template.New("custom") - t = t.Funcs(sprig.TxtFuncMap()) - t, err = t.Parse(templateText) - if err != nil { - return nil, fmt.Errorf("error parsing file: %w", err) - } - return &TemplateReporter{template: t}, nil -} - -// writeTemplate renders the findings using the user-provided template. -// https://www.digitalocean.com/community/tutorials/how-to-use-templates-in-go -func (t *TemplateReporter) Write(w io.WriteCloser, findings []Finding) error { - if err := t.template.Execute(w, findings); err != nil { - return err - } - return nil -} diff --git a/cli/detect/sources/directory.go b/cli/detect/sources/directory.go deleted file mode 100644 index 0ad46c3d8..000000000 --- a/cli/detect/sources/directory.go +++ /dev/null @@ -1,127 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package sources - -import ( - "io/fs" - "os" - "path/filepath" - "runtime" - - "github.com/fatih/semgroup" - - "github.com/Infisical/infisical-merge/detect/config" - "github.com/Infisical/infisical-merge/detect/logging" -) - -type ScanTarget struct { - Path string - Symlink string -} - -var isWindows = runtime.GOOS == "windows" - -func DirectoryTargets(source string, s *semgroup.Group, followSymlinks bool, allowlists []*config.Allowlist) (<-chan ScanTarget, error) { - paths := make(chan ScanTarget) - s.Go(func() error { - defer close(paths) - return filepath.Walk(source, - func(path string, fInfo os.FileInfo, err error) error { - logger := logging.With().Str("path", path).Logger() - - if err != nil { - if os.IsPermission(err) { - // This seems to only fail on directories at this stage. - logger.Warn().Msg("Skipping directory: permission denied") - return filepath.SkipDir - } - return err - } - - // Empty; nothing to do here. - if fInfo.Size() == 0 { - return nil - } - - // Unwrap symlinks, if |followSymlinks| is set. - scanTarget := ScanTarget{ - Path: path, - } - if fInfo.Mode().Type() == fs.ModeSymlink { - if !followSymlinks { - logger.Debug().Msg("Skipping symlink") - return nil - } - - realPath, err := filepath.EvalSymlinks(path) - if err != nil { - return err - } - - realPathFileInfo, _ := os.Stat(realPath) - if realPathFileInfo.IsDir() { - logger.Warn().Str("target", realPath).Msg("Skipping symlinked directory") - return nil - } - - scanTarget.Path = realPath - scanTarget.Symlink = path - } - - // TODO: Also run this check against the resolved symlink? - var skip bool - for _, a := range allowlists { - skip = a.PathAllowed(path) || - // TODO: Remove this in v9. - // This is an awkward hack to mitigate https://github.com/gitleaks/gitleaks/issues/1641. - (isWindows && a.PathAllowed(filepath.ToSlash(path))) - if skip { - break - } - } - if fInfo.IsDir() { - // Directory - if skip { - logger.Debug().Msg("Skipping directory due to global allowlist") - return filepath.SkipDir - } - - if fInfo.Name() == ".git" { - // Don't scan .git directories. - // TODO: Add this to the config allowlist, instead of hard-coding it. - return filepath.SkipDir - } - } else { - // File - if skip { - logger.Debug().Msg("Skipping file due to global allowlist") - return nil - } - - paths <- scanTarget - } - return nil - }) - }) - return paths, nil -} diff --git a/cli/detect/sources/git.go b/cli/detect/sources/git.go deleted file mode 100644 index 95b829a9a..000000000 --- a/cli/detect/sources/git.go +++ /dev/null @@ -1,211 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package sources - -import ( - "bufio" - "errors" - "io" - "os/exec" - "path/filepath" - "regexp" - "strings" - - "github.com/gitleaks/go-gitdiff/gitdiff" - - "github.com/Infisical/infisical-merge/detect/logging" -) - -var quotedOptPattern = regexp.MustCompile(`^(?:"[^"]+"|'[^']+')$`) - -// GitCmd helps to work with Git's output. -type GitCmd struct { - cmd *exec.Cmd - diffFilesCh <-chan *gitdiff.File - errCh <-chan error -} - -// NewGitLogCmd returns `*DiffFilesCmd` with two channels: `<-chan *gitdiff.File` and `<-chan error`. -// Caller should read everything from channels until receiving a signal about their closure and call -// the `func (*DiffFilesCmd) Wait()` error in order to release resources. -func NewGitLogCmd(source string, logOpts string) (*GitCmd, error) { - sourceClean := filepath.Clean(source) - var cmd *exec.Cmd - if logOpts != "" { - args := []string{"-C", sourceClean, "log", "-p", "-U0"} - - // Ensure that the user-provided |logOpts| aren't wrapped in quotes. - // https://github.com/gitleaks/gitleaks/issues/1153 - userArgs := strings.Split(logOpts, " ") - var quotedOpts []string - for _, element := range userArgs { - if quotedOptPattern.MatchString(element) { - quotedOpts = append(quotedOpts, element) - } - } - if len(quotedOpts) > 0 { - logging.Warn().Msgf("the following `--log-opts` values may not work as expected: %v\n\tsee https://github.com/gitleaks/gitleaks/issues/1153 for more information", quotedOpts) - } - - args = append(args, userArgs...) - cmd = exec.Command("git", args...) - } else { - cmd = exec.Command("git", "-C", sourceClean, "log", "-p", "-U0", - "--full-history", "--all") - } - - logging.Debug().Msgf("executing: %s", cmd.String()) - - stdout, err := cmd.StdoutPipe() - if err != nil { - return nil, err - } - stderr, err := cmd.StderrPipe() - if err != nil { - return nil, err - } - if err := cmd.Start(); err != nil { - return nil, err - } - - errCh := make(chan error) - go listenForStdErr(stderr, errCh) - - gitdiffFiles, err := gitdiff.Parse(stdout) - if err != nil { - return nil, err - } - - return &GitCmd{ - cmd: cmd, - diffFilesCh: gitdiffFiles, - errCh: errCh, - }, nil -} - -// NewGitDiffCmd returns `*DiffFilesCmd` with two channels: `<-chan *gitdiff.File` and `<-chan error`. -// Caller should read everything from channels until receiving a signal about their closure and call -// the `func (*DiffFilesCmd) Wait()` error in order to release resources. -func NewGitDiffCmd(source string, staged bool) (*GitCmd, error) { - sourceClean := filepath.Clean(source) - var cmd *exec.Cmd - cmd = exec.Command("git", "-C", sourceClean, "diff", "-U0", "--no-ext-diff", ".") - if staged { - cmd = exec.Command("git", "-C", sourceClean, "diff", "-U0", "--no-ext-diff", - "--staged", ".") - } - logging.Debug().Msgf("executing: %s", cmd.String()) - - stdout, err := cmd.StdoutPipe() - if err != nil { - return nil, err - } - stderr, err := cmd.StderrPipe() - if err != nil { - return nil, err - } - if err := cmd.Start(); err != nil { - return nil, err - } - - errCh := make(chan error) - go listenForStdErr(stderr, errCh) - - gitdiffFiles, err := gitdiff.Parse(stdout) - if err != nil { - return nil, err - } - - return &GitCmd{ - cmd: cmd, - diffFilesCh: gitdiffFiles, - errCh: errCh, - }, nil -} - -// DiffFilesCh returns a channel with *gitdiff.File. -func (c *GitCmd) DiffFilesCh() <-chan *gitdiff.File { - return c.diffFilesCh -} - -// ErrCh returns a channel that could produce an error if there is something in stderr. -func (c *GitCmd) ErrCh() <-chan error { - return c.errCh -} - -// Wait waits for the command to exit and waits for any copying to -// stdin or copying from stdout or stderr to complete. -// -// Wait also closes underlying stdout and stderr. -func (c *GitCmd) Wait() (err error) { - return c.cmd.Wait() -} - -// listenForStdErr listens for stderr output from git, prints it to stdout, -// sends to errCh and closes it. -func listenForStdErr(stderr io.ReadCloser, errCh chan<- error) { - defer close(errCh) - - var errEncountered bool - - scanner := bufio.NewScanner(stderr) - for scanner.Scan() { - // if git throws one of the following errors: - // - // exhaustive rename detection was skipped due to too many files. - // you may want to set your diff.renameLimit variable to at least - // (some large number) and retry the command. - // - // inexact rename detection was skipped due to too many files. - // you may want to set your diff.renameLimit variable to at least - // (some large number) and retry the command. - // - // Auto packing the repository in background for optimum performance. - // See "git help gc" for manual housekeeping. - // - // we skip exiting the program as git log -p/git diff will continue - // to send data to stdout and finish executing. This next bit of - // code prevents gitleaks from stopping mid scan if this error is - // encountered - if strings.Contains(scanner.Text(), - "exhaustive rename detection was skipped") || - strings.Contains(scanner.Text(), - "inexact rename detection was skipped") || - strings.Contains(scanner.Text(), - "you may want to set your diff.renameLimit") || - strings.Contains(scanner.Text(), - "See \"git help gc\" for manual housekeeping") || - strings.Contains(scanner.Text(), - "Auto packing the repository in background for optimum performance") { - logging.Warn().Msg(scanner.Text()) - } else { - logging.Error().Msgf("[git] %s", scanner.Text()) - errEncountered = true - } - } - - if errEncountered { - errCh <- errors.New("stderr is not empty") - return - } -} diff --git a/cli/detect/utils.go b/cli/detect/utils.go deleted file mode 100644 index 84b1017fc..000000000 --- a/cli/detect/utils.go +++ /dev/null @@ -1,280 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package detect - -import ( - // "encoding/json" - "fmt" - "math" - "path/filepath" - "strings" - "time" - - "github.com/Infisical/infisical-merge/detect/cmd/scm" - "github.com/Infisical/infisical-merge/detect/logging" - "github.com/Infisical/infisical-merge/detect/report" - - "github.com/charmbracelet/lipgloss" - "github.com/gitleaks/go-gitdiff/gitdiff" -) - -// augmentGitFinding updates the start and end line numbers of a finding to include the -// delta from the git diff -func augmentGitFinding(remote *RemoteInfo, finding report.Finding, textFragment *gitdiff.TextFragment, f *gitdiff.File) report.Finding { - if !strings.HasPrefix(finding.Match, "file detected") { - finding.StartLine += int(textFragment.NewPosition) - finding.EndLine += int(textFragment.NewPosition) - } - - if f.PatchHeader != nil { - finding.Commit = f.PatchHeader.SHA - if f.PatchHeader.Author != nil { - finding.Author = f.PatchHeader.Author.Name - finding.Email = f.PatchHeader.Author.Email - } - finding.Date = f.PatchHeader.AuthorDate.UTC().Format(time.RFC3339) - finding.Message = f.PatchHeader.Message() - // Results from `git diff` shouldn't have a link. - if finding.Commit != "" { - finding.Link = createScmLink(remote.Platform, remote.Url, finding) - } - } - return finding -} - -var linkCleaner = strings.NewReplacer( - " ", "%20", - "%", "%25", -) - -func createScmLink(scmPlatform scm.Platform, remoteUrl string, finding report.Finding) string { - if scmPlatform == scm.UnknownPlatform || scmPlatform == scm.NoPlatform { - return "" - } - - // Clean the path. - var ( - filePath = linkCleaner.Replace(finding.File) - ext = strings.ToLower(filepath.Ext(filePath)) - ) - - switch scmPlatform { - case scm.GitHubPlatform: - link := fmt.Sprintf("%s/blob/%s/%s", remoteUrl, finding.Commit, filePath) - if ext == ".ipynb" || ext == ".md" { - link += "?plain=1" - } - if finding.StartLine != 0 { - link += fmt.Sprintf("#L%d", finding.StartLine) - } - if finding.EndLine != finding.StartLine { - link += fmt.Sprintf("-L%d", finding.EndLine) - } - return link - case scm.GitLabPlatform: - link := fmt.Sprintf("%s/blob/%s/%s", remoteUrl, finding.Commit, filePath) - if finding.StartLine != 0 { - link += fmt.Sprintf("#L%d", finding.StartLine) - } - if finding.EndLine != finding.StartLine { - link += fmt.Sprintf("-%d", finding.EndLine) - } - return link - case scm.AzureDevOpsPlatform: - link := fmt.Sprintf("%s/commit/%s?path=/%s", remoteUrl, finding.Commit, filePath) - // Add line information if applicable - if finding.StartLine != 0 { - link += fmt.Sprintf("&line=%d", finding.StartLine) - } - if finding.EndLine != finding.StartLine { - link += fmt.Sprintf("&lineEnd=%d", finding.EndLine) - } - // This is a bit dirty, but Azure DevOps does not highlight the line when the lineStartColumn and lineEndColumn are not provided - link += "&lineStartColumn=1&lineEndColumn=10000000&type=2&lineStyle=plain&_a=files" - return link - case scm.BitBucketPlatform: - link := fmt.Sprintf("%s/src/%s/%s", remoteUrl, finding.Commit, filePath) - if finding.StartLine != 0 { - link += fmt.Sprintf("#lines-%d", finding.StartLine) - } - if finding.EndLine != finding.StartLine { - link += fmt.Sprintf(":%d", finding.EndLine) - } - return link - default: - // This should never happen. - return "" - } -} - -// shannonEntropy calculates the entropy of data using the formula defined here: -// https://en.wiktionary.org/wiki/Shannon_entropy -// Another way to think about what this is doing is calculating the number of bits -// needed to on average encode the data. So, the higher the entropy, the more random the data, the -// more bits needed to encode that data. -func shannonEntropy(data string) (entropy float64) { - if data == "" { - return 0 - } - - charCounts := make(map[rune]int) - for _, char := range data { - charCounts[char]++ - } - - invLength := 1.0 / float64(len(data)) - for _, count := range charCounts { - freq := float64(count) * invLength - entropy -= freq * math.Log2(freq) - } - - return entropy -} - -// filter will dedupe and redact findings -func filter(findings []report.Finding, redact uint) []report.Finding { - var retFindings []report.Finding - for _, f := range findings { - include := true - if strings.Contains(strings.ToLower(f.RuleID), "generic") { - for _, fPrime := range findings { - if f.StartLine == fPrime.StartLine && - f.Commit == fPrime.Commit && - f.RuleID != fPrime.RuleID && - strings.Contains(fPrime.Secret, f.Secret) && - !strings.Contains(strings.ToLower(fPrime.RuleID), "generic") { - - genericMatch := strings.Replace(f.Match, f.Secret, "REDACTED", -1) - betterMatch := strings.Replace(fPrime.Match, fPrime.Secret, "REDACTED", -1) - logging.Trace().Msgf("skipping %s finding (%s), %s rule takes precedence (%s)", f.RuleID, genericMatch, fPrime.RuleID, betterMatch) - include = false - break - } - } - } - - if redact > 0 { - f.Redact(redact) - } - if include { - retFindings = append(retFindings, f) - } - } - return retFindings -} - -func printFinding(f report.Finding, noColor bool) { - // trim all whitespace and tabs - f.Line = strings.TrimSpace(f.Line) - f.Secret = strings.TrimSpace(f.Secret) - f.Match = strings.TrimSpace(f.Match) - - isFileMatch := strings.HasPrefix(f.Match, "file detected:") - skipColor := noColor - finding := "" - var secret lipgloss.Style - - // Matches from filenames do not have a |line| or |secret| - if !isFileMatch { - matchInLineIDX := strings.Index(f.Line, f.Match) - secretInMatchIdx := strings.Index(f.Match, f.Secret) - - skipColor = false - - if matchInLineIDX == -1 || noColor { - skipColor = true - matchInLineIDX = 0 - } - - start := f.Line[0:matchInLineIDX] - startMatchIdx := 0 - if matchInLineIDX > 20 { - startMatchIdx = matchInLineIDX - 20 - start = "..." + f.Line[startMatchIdx:matchInLineIDX] - } - - matchBeginning := lipgloss.NewStyle().SetString(f.Match[0:secretInMatchIdx]).Foreground(lipgloss.Color("#f5d445")) - secret = lipgloss.NewStyle().SetString(f.Secret). - Bold(true). - Italic(true). - Foreground(lipgloss.Color("#f05c07")) - matchEnd := lipgloss.NewStyle().SetString(f.Match[secretInMatchIdx+len(f.Secret):]).Foreground(lipgloss.Color("#f5d445")) - - lineEndIdx := matchInLineIDX + len(f.Match) - if len(f.Line)-1 <= lineEndIdx { - lineEndIdx = len(f.Line) - } - - lineEnd := f.Line[lineEndIdx:] - - if len(f.Secret) > 100 { - secret = lipgloss.NewStyle().SetString(f.Secret[0:100] + "..."). - Bold(true). - Italic(true). - Foreground(lipgloss.Color("#f05c07")) - } - if len(lineEnd) > 20 { - lineEnd = lineEnd[0:20] + "..." - } - - finding = fmt.Sprintf("%s%s%s%s%s\n", strings.TrimPrefix(strings.TrimLeft(start, " "), "\n"), matchBeginning, secret, matchEnd, lineEnd) - } - - if skipColor || isFileMatch { - fmt.Printf("%-12s %s\n", "Finding:", f.Match) - fmt.Printf("%-12s %s\n", "Secret:", f.Secret) - } else { - fmt.Printf("%-12s %s", "Finding:", finding) - fmt.Printf("%-12s %s\n", "Secret:", secret) - } - - fmt.Printf("%-12s %s\n", "RuleID:", f.RuleID) - fmt.Printf("%-12s %f\n", "Entropy:", f.Entropy) - if f.File == "" { - fmt.Println("") - return - } - if len(f.Tags) > 0 { - fmt.Printf("%-12s %s\n", "Tags:", f.Tags) - } - fmt.Printf("%-12s %s\n", "File:", f.File) - fmt.Printf("%-12s %d\n", "Line:", f.StartLine) - if f.Commit == "" { - fmt.Printf("%-12s %s\n", "Fingerprint:", f.Fingerprint) - fmt.Println("") - return - } - fmt.Printf("%-12s %s\n", "Commit:", f.Commit) - fmt.Printf("%-12s %s\n", "Author:", f.Author) - fmt.Printf("%-12s %s\n", "Email:", f.Email) - fmt.Printf("%-12s %s\n", "Date:", f.Date) - fmt.Printf("%-12s %s\n", "Fingerprint:", f.Fingerprint) - if f.Link != "" { - fmt.Printf("%-12s %s\n", "Link:", f.Link) - } - fmt.Println("") -} - -func isWhitespace(ch byte) bool { - return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' -} diff --git a/cli/docker/alpine b/cli/docker/alpine deleted file mode 100644 index e1a59d6eb..000000000 --- a/cli/docker/alpine +++ /dev/null @@ -1,9 +0,0 @@ -FROM alpine -RUN apk add --no-cache tini - -## Upgrade OpenSSL libraries to mitigate known vulnerabilities as the current Alpine image has not been patched yet. -RUN apk update && apk upgrade --no-cache libcrypto3 libssl3 - - -COPY infisical /bin/infisical -ENTRYPOINT ["/sbin/tini", "--", "/bin/infisical"] \ No newline at end of file diff --git a/cli/go.mod b/cli/go.mod deleted file mode 100644 index 3afc8d3be..000000000 --- a/cli/go.mod +++ /dev/null @@ -1,183 +0,0 @@ -module github.com/Infisical/infisical-merge - -go 1.23.0 - -toolchain go1.23.5 - -require ( - github.com/BobuSumisu/aho-corasick v1.0.3 - github.com/Masterminds/sprig/v3 v3.3.0 - github.com/bradleyjkemp/cupaloy/v2 v2.8.0 - github.com/charmbracelet/lipgloss v0.9.1 - github.com/creack/pty v1.1.21 - github.com/denisbrodbeck/machineid v1.0.1 - github.com/fatih/semgroup v1.2.0 - github.com/gitleaks/go-gitdiff v0.9.1 - github.com/h2non/filetype v1.1.3 - github.com/infisical/go-sdk v0.5.96 - github.com/infisical/infisical-kmip v0.3.5 - github.com/mattn/go-isatty v0.0.20 - github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a - github.com/muesli/mango-cobra v1.2.0 - github.com/muesli/reflow v0.3.0 - github.com/muesli/roff v0.1.0 - github.com/pion/dtls/v3 v3.0.4 - github.com/pion/logging v0.2.3 - github.com/pion/turn/v4 v4.0.0 - github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c - github.com/pkg/errors v0.9.1 - github.com/posthog/posthog-go v0.0.0-20221221115252-24dfed35d71a - github.com/quic-go/quic-go v0.50.0 - github.com/rs/cors v1.11.0 - github.com/rs/zerolog v1.26.1 - github.com/spf13/cobra v1.6.1 - github.com/spf13/viper v1.8.1 - github.com/stretchr/testify v1.10.0 - github.com/wasilibs/go-re2 v1.10.0 - golang.org/x/crypto v0.36.0 - golang.org/x/exp v0.0.0-20250228200357-dead58393ab7 - golang.org/x/sys v0.31.0 - golang.org/x/term v0.30.0 - gopkg.in/yaml.v2 v2.4.0 - gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.31.4 - k8s.io/apimachinery v0.31.4 - k8s.io/client-go v0.31.4 -) - -require ( - cloud.google.com/go/auth v0.7.0 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect - cloud.google.com/go/compute/metadata v0.4.0 // indirect - cloud.google.com/go/iam v1.1.11 // indirect - dario.cat/mergo v1.0.1 // indirect - github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.3.0 // indirect - github.com/alessio/shellescape v1.4.1 // indirect - github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect - github.com/aws/aws-sdk-go-v2 v1.27.2 // indirect - github.com/aws/aws-sdk-go-v2/config v1.27.18 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.17.18 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.5 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.9 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.9 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.11 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.20.11 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.5 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.28.12 // indirect - github.com/aws/smithy-go v1.20.2 // indirect - github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/chzyer/readline v1.5.1 // indirect - github.com/danieljoos/wincred v1.2.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/dvsekhvalnov/jose2go v1.6.0 // indirect - github.com/emicklei/go-restful/v3 v3.11.0 // indirect - github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.4.9 // indirect - github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/go-logr/logr v1.4.2 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/errors v0.20.2 // indirect - github.com/go-openapi/jsonpointer v0.21.0 // indirect - github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/strfmt v0.21.3 // indirect - github.com/go-openapi/swag v0.23.0 // indirect - github.com/go-task/slim-sprig/v3 v3.0.0 // indirect - github.com/godbus/dbus/v5 v5.1.0 // indirect - github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.4 // indirect - github.com/google/gnostic-models v0.6.9 // indirect - github.com/google/go-cmp v0.7.0 // indirect - github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20250302191652-9094ed2288e7 // indirect - github.com/google/s2a-go v0.1.7 // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect - github.com/googleapis/gax-go/v2 v2.12.5 // indirect - github.com/gosimple/slug v1.15.0 // indirect - github.com/gosimple/unidecode v1.0.1 // indirect - github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect - github.com/huandu/xstrings v1.5.0 // indirect - github.com/josharian/intern v1.0.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/magiconair/properties v1.8.5 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/mapstructure v1.4.1 // indirect - github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/mtibben/percent v0.2.1 // indirect - github.com/muesli/mango v0.1.0 // indirect - github.com/muesli/mango-pflag v0.1.0 // indirect - github.com/muesli/termenv v0.15.2 // indirect - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/oklog/ulid v1.3.1 // indirect - github.com/onsi/ginkgo/v2 v2.22.2 // indirect - github.com/pelletier/go-toml v1.9.3 // indirect - github.com/pion/randutil v0.1.0 // indirect - github.com/pion/stun/v3 v3.0.0 // indirect - github.com/pion/transport/v3 v3.0.7 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rivo/uniseg v0.2.0 // indirect - github.com/shopspring/decimal v1.4.0 // indirect - github.com/spf13/afero v1.6.0 // indirect - github.com/spf13/cast v1.7.0 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/subosito/gotenv v1.2.0 // indirect - github.com/tetratelabs/wazero v1.9.0 // indirect - github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 // indirect - github.com/wlynxg/anet v0.0.5 // indirect - github.com/x448/float16 v0.8.4 // indirect - github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect - go.mongodb.org/mongo-driver v1.10.0 // indirect - go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect - go.opentelemetry.io/otel v1.24.0 // indirect - go.opentelemetry.io/otel/metric v1.24.0 // indirect - go.opentelemetry.io/otel/trace v1.24.0 // indirect - go.uber.org/mock v0.5.0 // indirect - golang.org/x/mod v0.23.0 // indirect - golang.org/x/net v0.38.0 // indirect - golang.org/x/oauth2 v0.27.0 // indirect - golang.org/x/sync v0.12.0 // indirect - golang.org/x/text v0.23.0 // indirect - golang.org/x/time v0.9.0 // indirect - golang.org/x/tools v0.30.0 // indirect - google.golang.org/api v0.188.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240708141625-4ad9e859172b // indirect - google.golang.org/grpc v1.64.1 // indirect - google.golang.org/protobuf v1.36.5 // indirect - gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/ini.v1 v1.62.0 // indirect - k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect - k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect - sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect - sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect -) - -require ( - github.com/fatih/color v1.17.0 - github.com/go-resty/resty/v2 v2.16.5 - github.com/inconshreveable/mousetrap v1.0.1 // indirect - github.com/jedib0t/go-pretty v4.3.0+incompatible - github.com/manifoldco/promptui v0.9.0 - github.com/spf13/pflag v1.0.5 // indirect - github.com/zalando/go-keyring v0.2.3 -) - -replace github.com/zalando/go-keyring => github.com/Infisical/go-keyring v1.0.2 - -replace github.com/pion/turn/v4 => github.com/Infisical/turn/v4 v4.0.1 diff --git a/cli/go.sum b/cli/go.sum deleted file mode 100644 index 066f736a2..000000000 --- a/cli/go.sum +++ /dev/null @@ -1,951 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go/auth v0.7.0 h1:kf/x9B3WTbBUHkC+1VS8wwwli9TzhSt0vSTVBmMR8Ts= -cloud.google.com/go/auth v0.7.0/go.mod h1:D+WqdrpcjmiCgWrXmLLxOVq1GACoE36chW6KXoEvuIw= -cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4= -cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute/metadata v0.4.0 h1:vHzJCWaM4g8XIcm8kopr3XmDA4Gy/lblD3EhhSux05c= -cloud.google.com/go/compute/metadata v0.4.0/go.mod h1:SIQh1Kkb4ZJ8zJ874fqVkslA29PRXuleyj6vOzlbK7M= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/iam v1.1.11 h1:0mQ8UKSfdHLut6pH9FM3bI55KWR46ketn0PuXleDyxw= -cloud.google.com/go/iam v1.1.11/go.mod h1:biXoiLWYIKntto2joP+62sd9uW5EpkZmKIvfNcTWlnQ= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= -dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BobuSumisu/aho-corasick v1.0.3 h1:uuf+JHwU9CHP2Vx+wAy6jcksJThhJS9ehR8a+4nPE9g= -github.com/BobuSumisu/aho-corasick v1.0.3/go.mod h1:hm4jLcvZKI2vRF2WDU1N4p/jpWtpOzp3nLmi9AzX/XE= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/Infisical/go-keyring v1.0.2 h1:dWOkI/pB/7RocfSJgGXbXxLDcVYsdslgjEPmVhb+nl8= -github.com/Infisical/go-keyring v1.0.2/go.mod h1:LWOnn/sw9FxDW/0VY+jHFAfOFEe03xmwBVSfJnBowto= -github.com/Infisical/turn/v4 v4.0.1 h1:omdelNsnFfzS5cu86W5OBR68by68a8sva4ogR0lQQnw= -github.com/Infisical/turn/v4 v4.0.1/go.mod h1:pMMKP/ieNAG/fN5cZiN4SDuyKsXtNTr0ccN7IToA1zs= -github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= -github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= -github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= -github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= -github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= -github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= -github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef h1:46PFijGLmAjMPwCCCo7Jf0W6f9slllCkkv7vyc1yOSg= -github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go-v2 v1.27.2 h1:pLsTXqX93rimAOZG2FIYraDQstZaaGVVN4tNw65v0h8= -github.com/aws/aws-sdk-go-v2 v1.27.2/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM= -github.com/aws/aws-sdk-go-v2/config v1.27.18 h1:wFvAnwOKKe7QAyIxziwSKjmer9JBMH1vzIL6W+fYuKk= -github.com/aws/aws-sdk-go-v2/config v1.27.18/go.mod h1:0xz6cgdX55+kmppvPm2IaKzIXOheGJhAufacPJaXZ7c= -github.com/aws/aws-sdk-go-v2/credentials v1.17.18 h1:D/ALDWqK4JdY3OFgA2thcPO1c9aYTT5STS/CvnkqY1c= -github.com/aws/aws-sdk-go-v2/credentials v1.17.18/go.mod h1:JuitCWq+F5QGUrmMPsk945rop6bB57jdscu+Glozdnc= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.5 h1:dDgptDO9dxeFkXy+tEgVkzSClHZje/6JkPW5aZyEvrQ= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.5/go.mod h1:gjvE2KBUgUQhcv89jqxrIxH9GaKs1JbZzWejj/DaHGA= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.9 h1:cy8ahBJuhtM8GTTSyOkfy6WVPV1IE+SS5/wfXUYuulw= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.9/go.mod h1:CZBXGLaJnEZI6EVNcPd7a6B5IC5cA/GkRWtu9fp3S6Y= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.9 h1:A4SYk07ef04+vxZToz9LWvAXl9LW0NClpPpMsi31cz0= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.9/go.mod h1:5jJcHuwDagxN+ErjQ3PU3ocf6Ylc/p9x+BLO/+X4iXw= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.11 h1:o4T+fKxA3gTMcluBNZZXE9DNaMkJuUL1O3mffCUjoJo= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.11/go.mod h1:84oZdJ+VjuJKs9v1UTC9NaodRZRseOXCTgku+vQJWR8= -github.com/aws/aws-sdk-go-v2/service/sso v1.20.11 h1:gEYM2GSpr4YNWc6hCd5nod4+d4kd9vWIAWrmGuLdlMw= -github.com/aws/aws-sdk-go-v2/service/sso v1.20.11/go.mod h1:gVvwPdPNYehHSP9Rs7q27U1EU+3Or2ZpXvzAYJNh63w= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.5 h1:iXjh3uaH3vsVcnyZX7MqCoCfcyxIrVE9iOQruRaWPrQ= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.5/go.mod h1:5ZXesEuy/QcO0WUnt+4sDkxhdXRHTu2yG0uCSH8B6os= -github.com/aws/aws-sdk-go-v2/service/sts v1.28.12 h1:M/1u4HBpwLuMtjlxuI2y6HoVLzF5e2mfxHCg7ZVMYmk= -github.com/aws/aws-sdk-go-v2/service/sts v1.28.12/go.mod h1:kcfd+eTdEi/40FIbLq4Hif3XMXnl5b/+t/KTfLt9xIk= -github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q= -github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= -github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= -github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= -github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M= -github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg= -github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= -github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= -github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= -github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -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/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= -github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE= -github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/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.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA2EjTY= -github.com/dvsekhvalnov/jose2go v1.6.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= -github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= -github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= -github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= -github.com/fatih/semgroup v1.2.0 h1:h/OLXwEM+3NNyAdZEpMiH1OzfplU09i2qXPVThGZvyg= -github.com/fatih/semgroup v1.2.0/go.mod h1:1KAD4iIYfXjE4U13B48VM4z9QUwV5Tt8O4rS879kgm8= -github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= -github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= -github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gitleaks/go-gitdiff v0.9.1 h1:ni6z6/3i9ODT685OLCTf+s/ERlWUNWQF4x1pvoNICw0= -github.com/gitleaks/go-gitdiff v0.9.1/go.mod h1:pKz0X4YzCKZs30BL+weqBIG7mx0jl4tF1uXV9ZyNvrA= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/errors v0.20.2 h1:dxy7PGTqEh94zj2E3h1cUmQQWiM1+aeCROfAr02EmK8= -github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= -github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= -github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= -github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/strfmt v0.21.3 h1:xwhj5X6CjXEZZHMWy1zKJxvW9AfHC9pkyUjLvHtKG7o= -github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= -github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM= -github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA= -github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= -github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= -github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= -github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20250302191652-9094ed2288e7 h1:+J3r2e8+RsmN3vKfo75g0YSY61ms37qzPglu4p0sGro= -github.com/google/pprof v0.0.0-20250302191652-9094ed2288e7/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= -github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= -github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.12.5 h1:8gw9KZK8TiVKB6q3zHY3SBzLnrGp6HQjyfYBYGmXdxA= -github.com/googleapis/gax-go/v2 v2.12.5/go.mod h1:BUDKcWo+RaKq5SC9vVYL0wLADa3VcfswbOMMRmB9H3E= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gosimple/slug v1.15.0 h1:wRZHsRrRcs6b0XnxMUBM6WK1U1Vg5B0R7VkIf1Xzobo= -github.com/gosimple/slug v1.15.0/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= -github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= -github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg= -github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= -github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= -github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/infisical/go-sdk v0.5.96 h1:huky6bQ1Y3oRdPb5MO3Ru868qZaPHUxZ7kP7FPNRn48= -github.com/infisical/go-sdk v0.5.96/go.mod h1:ExjqFLRz7LSpZpGluqDLvFl6dFBLq5LKyLW7GBaMAIs= -github.com/infisical/infisical-kmip v0.3.5 h1:QM3s0e18B+mYv3a9HQNjNAlbwZJBzXq5BAJM2scIeiE= -github.com/infisical/infisical-kmip v0.3.5/go.mod h1:bO1M4YtKyutNg1bREPmlyZspC5duSR7hyQ3lPmLzrIs= -github.com/jedib0t/go-pretty v4.3.0+incompatible h1:CGs8AVhEKg/n9YbUenWmNStRW2PHJzaeDodcfvRAbIo= -github.com/jedib0t/go-pretty v4.3.0+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= -github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= -github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= -github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= -github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= -github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= -github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= -github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a h1:jlDOeO5TU0pYlbc/y6PFguab5IjANI0Knrpg3u/ton4= -github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= -github.com/muesli/mango v0.1.0 h1:DZQK45d2gGbql1arsYA4vfg4d7I9Hfx5rX/GCmzsAvI= -github.com/muesli/mango v0.1.0/go.mod h1:5XFpbC8jY5UUv89YQciiXNlbi+iJgt29VDC5xbzrLL4= -github.com/muesli/mango-cobra v1.2.0 h1:DQvjzAM0PMZr85Iv9LIMaYISpTOliMEg+uMFtNbYvWg= -github.com/muesli/mango-cobra v1.2.0/go.mod h1:vMJL54QytZAJhCT13LPVDfkvCUJ5/4jNUKF/8NC2UjA= -github.com/muesli/mango-pflag v0.1.0 h1:UADqbYgpUyRoBja3g6LUL+3LErjpsOwaC9ywvBWe7Sg= -github.com/muesli/mango-pflag v0.1.0/go.mod h1:YEQomTxaCUp8PrbhFh10UfbhbQrM/xJ4i2PB8VTLLW0= -github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= -github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= -github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8= -github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig= -github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= -github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU= -github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= -github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= -github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pion/dtls/v3 v3.0.4 h1:44CZekewMzfrn9pmGrj5BNnTMDCFwr+6sLH+cCuLM7U= -github.com/pion/dtls/v3 v3.0.4/go.mod h1:R373CsjxWqNPf6MEkfdy3aSe9niZvL/JaKlGeFphtMg= -github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI= -github.com/pion/logging v0.2.3/go.mod h1:z8YfknkquMe1csOrxK5kc+5/ZPAzMxbKLX5aXpbpC90= -github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= -github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= -github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw= -github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU= -github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= -github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= -github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= -github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/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/quic-go/quic-go v0.50.0 h1:3H/ld1pa3CYhkcc20TPIyG1bNsdhn9qZBGN3b9/UyUo= -github.com/quic-go/quic-go v0.50.0/go.mod h1:Vim6OmUvlYdwBhXP9ZVrtGmCMWa3wEqhq3NgYrI8b4E= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po= -github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= -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/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= -github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= -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= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= -github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= -github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44= -github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -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/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I= -github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM= -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/wasilibs/go-re2 v1.10.0 h1:vQZEBYZOCA9jdBMmrO4+CvqyCj0x4OomXTJ4a5/urQ0= -github.com/wasilibs/go-re2 v1.10.0/go.mod h1:k+5XqO2bCJS+QpGOnqugyfwC04nw0jaglmjrrkG8U6o= -github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 h1:OvLBa8SqJnZ6P+mjlzc2K7PM22rRUPE1x32G9DTPrC4= -github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52/go.mod h1:jMeV4Vpbi8osrE/pKUxRZkVaA0EX7NZN0A9/oRzgpgY= -github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= -github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= -github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= -github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -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= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.mongodb.org/mongo-driver v1.10.0 h1:UtV6N5k14upNp4LTduX0QCufG124fSu25Wz9tu94GLg= -go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= -go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= -go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= -go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= -go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= -go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= -go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= -go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= -golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20250228200357-dead58393ab7 h1:aWwlzYV971S4BXRS9AmqwDLAD85ouC6X+pocatKY58c= -golang.org/x/exp v0.0.0-20250228200357-dead58393ab7/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= -golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= -golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= -golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= -golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= -golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= -golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= -golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= -google.golang.org/api v0.188.0 h1:51y8fJ/b1AaaBRJr4yWm96fPcuxSo0JcegXE3DaHQHw= -google.golang.org/api v0.188.0/go.mod h1:VR0d+2SIiWOYG3r/jdm7adPW9hI2aRv9ETOSCQ9Beag= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0= -google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240708141625-4ad9e859172b h1:04+jVzTs2XBnOZcPsLnmrTGqltqJbZQ1Ey26hjYdQQ0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240708141625-4ad9e859172b/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= -google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= -google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= -gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.31.4 h1:I2QNzitPVsPeLQvexMEsj945QumYraqv9m74isPDKhM= -k8s.io/api v0.31.4/go.mod h1:d+7vgXLvmcdT1BCo79VEgJxHHryww3V5np2OYTr6jdw= -k8s.io/apimachinery v0.31.4 h1:8xjE2C4CzhYVm9DGf60yohpNUh5AEBnPxCryPBECmlM= -k8s.io/apimachinery v0.31.4/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/client-go v0.31.4 h1:t4QEXt4jgHIkKKlx06+W3+1JOwAFU/2OPiOo7H92eRQ= -k8s.io/client-go v0.31.4/go.mod h1:kvuMro4sFYIa8sulL5Gi5GFqUPvfH2O/dXuKstbaaeg= -k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= -k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= -k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= -k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= -k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= -sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= -sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= -sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= -sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= -sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/cli/goreleaser.dockerfile b/cli/goreleaser.dockerfile deleted file mode 100644 index 0436d4d8e..000000000 --- a/cli/goreleaser.dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -FROM alpine -RUN apk add --no-cache tini -COPY infisical /bin/infisical -ENTRYPOINT ["/sbin/tini", "--", "/bin/infisical"] \ No newline at end of file diff --git a/cli/infisical-cli.repo b/cli/infisical-cli.repo deleted file mode 100644 index 74c39daca..000000000 --- a/cli/infisical-cli.repo +++ /dev/null @@ -1,5 +0,0 @@ -[infisical] -name=Infisical CLI -baseurl=https://yum.fury.io/infisical/ -enabled=1 -gpgcheck=0 \ No newline at end of file diff --git a/cli/main.go b/cli/main.go deleted file mode 100644 index 75152ffc4..000000000 --- a/cli/main.go +++ /dev/null @@ -1,17 +0,0 @@ -/* -Copyright (c) 2023 Infisical Inc. -*/ -package main - -import ( - "os" - - "github.com/Infisical/infisical-merge/packages/cmd" - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" -) - -func main() { - log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) - cmd.Execute() -} diff --git a/cli/packages/api/api.go b/cli/packages/api/api.go deleted file mode 100644 index 15f75a57d..000000000 --- a/cli/packages/api/api.go +++ /dev/null @@ -1,652 +0,0 @@ -package api - -import ( - "fmt" - "net/http" - "strings" - - "github.com/Infisical/infisical-merge/packages/config" - "github.com/go-resty/resty/v2" - "github.com/rs/zerolog/log" -) - -const USER_AGENT = "cli" - -const ( - operationCallGetRawSecretsV3 = "CallGetRawSecretsV3" - operationCallGetEncryptedWorkspaceKey = "CallGetEncryptedWorkspaceKey" - operationCallGetServiceTokenDetails = "CallGetServiceTokenDetails" - operationCallLogin1V3 = "CallLogin1V3" - operationCallVerifyMfaToken = "CallVerifyMfaToken" - operationCallLogin2V3 = "CallLogin2V3" - operationCallGetAllOrganizations = "CallGetAllOrganizations" - operationCallSelectOrganization = "CallSelectOrganization" - operationCallGetAllWorkSpacesUserBelongsTo = "CallGetAllWorkSpacesUserBelongsTo" - operationCallGetProjectById = "CallGetProjectById" - operationCallIsAuthenticated = "CallIsAuthenticated" - operationCallGetNewAccessTokenWithRefreshToken = "CallGetNewAccessTokenWithRefreshToken" - operationCallGetFoldersV1 = "CallGetFoldersV1" - operationCallCreateFolderV1 = "CallCreateFolderV1" - operationCallDeleteFolderV1 = "CallDeleteFolderV1" - operationCallDeleteSecretsV3 = "CallDeleteSecretsV3" - operationCallCreateServiceToken = "CallCreateServiceToken" - operationCallUniversalAuthLogin = "CallUniversalAuthLogin" - operationCallMachineIdentityRefreshAccessToken = "CallMachineIdentityRefreshAccessToken" - operationCallFetchSingleSecretByName = "CallFetchSingleSecretByName" - operationCallCreateRawSecretsV3 = "CallCreateRawSecretsV3" - operationCallUpdateRawSecretsV3 = "CallUpdateRawSecretsV3" - operationCallRegisterGatewayIdentityV1 = "CallRegisterGatewayIdentityV1" - operationCallExchangeRelayCertV1 = "CallExchangeRelayCertV1" - operationCallGatewayHeartBeatV1 = "CallGatewayHeartBeatV1" - operationCallBootstrapInstance = "CallBootstrapInstance" -) - -func CallGetEncryptedWorkspaceKey(httpClient *resty.Client, request GetEncryptedWorkspaceKeyRequest) (GetEncryptedWorkspaceKeyResponse, error) { - endpoint := fmt.Sprintf("%v/v2/workspace/%v/encrypted-key", config.INFISICAL_URL, request.WorkspaceId) - var result GetEncryptedWorkspaceKeyResponse - response, err := httpClient. - R(). - SetResult(&result). - SetHeader("User-Agent", USER_AGENT). - Get(endpoint) - - if err != nil { - return GetEncryptedWorkspaceKeyResponse{}, NewGenericRequestError(operationCallGetEncryptedWorkspaceKey, err) - } - - if response.IsError() { - return GetEncryptedWorkspaceKeyResponse{}, NewAPIErrorWithResponse(operationCallGetEncryptedWorkspaceKey, response, nil) - } - - return result, nil -} - -func CallGetServiceTokenDetailsV2(httpClient *resty.Client) (GetServiceTokenDetailsResponse, error) { - var tokenDetailsResponse GetServiceTokenDetailsResponse - response, err := httpClient. - R(). - SetResult(&tokenDetailsResponse). - SetHeader("User-Agent", USER_AGENT). - Get(fmt.Sprintf("%v/v2/service-token", config.INFISICAL_URL)) - - if err != nil { - return GetServiceTokenDetailsResponse{}, NewGenericRequestError(operationCallGetServiceTokenDetails, err) - } - - if response.IsError() { - return GetServiceTokenDetailsResponse{}, NewAPIErrorWithResponse(operationCallGetServiceTokenDetails, response, nil) - } - - return tokenDetailsResponse, nil -} - -func CallLogin1V2(httpClient *resty.Client, request GetLoginOneV2Request) (GetLoginOneV2Response, error) { - var loginOneV2Response GetLoginOneV2Response - response, err := httpClient. - R(). - SetResult(&loginOneV2Response). - SetHeader("User-Agent", USER_AGENT). - SetBody(request). - Post(fmt.Sprintf("%v/v3/auth/login1", config.INFISICAL_URL)) - - if err != nil { - return GetLoginOneV2Response{}, NewGenericRequestError(operationCallLogin1V3, err) - } - - if response.IsError() { - return GetLoginOneV2Response{}, NewAPIErrorWithResponse(operationCallLogin1V3, response, nil) - } - - return loginOneV2Response, nil -} - -func CallVerifyMfaToken(httpClient *resty.Client, request VerifyMfaTokenRequest) (*VerifyMfaTokenResponse, *VerifyMfaTokenErrorResponse, error) { - var verifyMfaTokenResponse VerifyMfaTokenResponse - var responseError VerifyMfaTokenErrorResponse - response, err := httpClient. - R(). - SetResult(&verifyMfaTokenResponse). - SetHeader("User-Agent", USER_AGENT). - SetError(&responseError). - SetBody(request). - Post(fmt.Sprintf("%v/v2/auth/mfa/verify", 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 - } - } - - // When MFA is enabled - if refreshToken != nil { - verifyMfaTokenResponse.RefreshToken = refreshToken.Value - } - - if err != nil { - return nil, nil, NewGenericRequestError(operationCallVerifyMfaToken, err) - } - - if response.IsError() { - return nil, &responseError, nil - } - - return &verifyMfaTokenResponse, nil, nil -} - -func CallLogin2V2(httpClient *resty.Client, request GetLoginTwoV2Request) (GetLoginTwoV2Response, error) { - var loginTwoV2Response GetLoginTwoV2Response - response, err := httpClient. - R(). - SetResult(&loginTwoV2Response). - SetHeader("User-Agent", USER_AGENT). - SetBody(request). - Post(fmt.Sprintf("%v/v3/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 - } - } - - // When MFA is enabled - if refreshToken != nil { - loginTwoV2Response.RefreshToken = refreshToken.Value - } - - if err != nil { - return GetLoginTwoV2Response{}, NewGenericRequestError(operationCallLogin2V3, err) - } - - if response.IsError() { - return GetLoginTwoV2Response{}, NewAPIErrorWithResponse(operationCallLogin2V3, response, nil) - } - - return loginTwoV2Response, nil -} - -func CallGetAllOrganizations(httpClient *resty.Client) (GetOrganizationsResponse, error) { - var orgResponse GetOrganizationsResponse - response, err := httpClient. - R(). - SetResult(&orgResponse). - SetHeader("User-Agent", USER_AGENT). - Get(fmt.Sprintf("%v/v1/organization", config.INFISICAL_URL)) - - if err != nil { - return GetOrganizationsResponse{}, NewGenericRequestError(operationCallGetAllOrganizations, err) - } - - if response.IsError() { - return GetOrganizationsResponse{}, NewAPIErrorWithResponse(operationCallGetAllOrganizations, response, nil) - } - - return orgResponse, nil -} - -func CallSelectOrganization(httpClient *resty.Client, request SelectOrganizationRequest) (SelectOrganizationResponse, error) { - var selectOrgResponse SelectOrganizationResponse - - response, err := httpClient. - R(). - SetBody(request). - SetResult(&selectOrgResponse). - SetHeader("User-Agent", USER_AGENT). - Post(fmt.Sprintf("%v/v3/auth/select-organization", config.INFISICAL_URL)) - - if err != nil { - return SelectOrganizationResponse{}, NewGenericRequestError(operationCallSelectOrganization, err) - } - - if response.IsError() { - return SelectOrganizationResponse{}, NewAPIErrorWithResponse(operationCallSelectOrganization, response, nil) - } - - return selectOrgResponse, nil - -} - -func CallGetAllWorkSpacesUserBelongsTo(httpClient *resty.Client) (GetWorkSpacesResponse, error) { - var workSpacesResponse GetWorkSpacesResponse - response, err := httpClient. - R(). - SetResult(&workSpacesResponse). - SetHeader("User-Agent", USER_AGENT). - Get(fmt.Sprintf("%v/v1/workspace", config.INFISICAL_URL)) - - if err != nil { - return GetWorkSpacesResponse{}, err - } - - if response.IsError() { - return GetWorkSpacesResponse{}, fmt.Errorf("CallGetAllWorkSpacesUserBelongsTo: Unsuccessful response: [response=%v]", response) - } - - return workSpacesResponse, nil -} - -func CallGetProjectById(httpClient *resty.Client, id string) (Project, error) { - var projectResponse GetProjectByIdResponse - response, err := httpClient. - R(). - SetResult(&projectResponse). - SetHeader("User-Agent", USER_AGENT). - Get(fmt.Sprintf("%v/v1/workspace/%s", config.INFISICAL_URL, id)) - - if err != nil { - return Project{}, NewGenericRequestError(operationCallGetProjectById, err) - } - - if response.IsError() { - return Project{}, NewAPIErrorWithResponse(operationCallGetProjectById, response, nil) - } - - return projectResponse.Project, nil -} - -func CallIsAuthenticated(httpClient *resty.Client) bool { - var workSpacesResponse GetWorkSpacesResponse - response, err := httpClient. - R(). - SetResult(&workSpacesResponse). - SetHeader("User-Agent", USER_AGENT). - Post(fmt.Sprintf("%v/v1/auth/checkAuth", config.INFISICAL_URL)) - - if err != nil { - return false - } - - if response.IsError() { - log.Debug().Msgf("%s: Unsuccessful response: [response=%v]", operationCallIsAuthenticated, response) - return false - } - - return true -} - -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{}, NewGenericRequestError(operationCallGetNewAccessTokenWithRefreshToken, err) - } - - if response.IsError() { - return GetNewAccessTokenWithRefreshTokenResponse{}, NewAPIErrorWithResponse(operationCallGetNewAccessTokenWithRefreshToken, response, nil) - } - - return newAccessToken, nil -} - -func CallGetFoldersV1(httpClient *resty.Client, request GetFoldersV1Request) (GetFoldersV1Response, error) { - var foldersResponse GetFoldersV1Response - httpRequest := httpClient. - R(). - SetResult(&foldersResponse). - SetHeader("User-Agent", USER_AGENT). - SetQueryParam("environment", request.Environment). - SetQueryParam("workspaceId", request.WorkspaceId). - SetQueryParam("directory", request.FoldersPath) - - response, err := httpRequest.Get(fmt.Sprintf("%v/v1/folders", config.INFISICAL_URL)) - - if err != nil { - return GetFoldersV1Response{}, NewGenericRequestError(operationCallGetFoldersV1, err) - } - - if response.IsError() { - return GetFoldersV1Response{}, NewAPIErrorWithResponse(operationCallGetFoldersV1, response, nil) - } - - return foldersResponse, nil -} - -func CallCreateFolderV1(httpClient *resty.Client, request CreateFolderV1Request) (CreateFolderV1Response, error) { - var folderResponse CreateFolderV1Response - httpRequest := httpClient. - R(). - SetResult(&folderResponse). - SetHeader("User-Agent", USER_AGENT). - SetBody(request) - - response, err := httpRequest.Post(fmt.Sprintf("%v/v1/folders", config.INFISICAL_URL)) - if err != nil { - return CreateFolderV1Response{}, NewGenericRequestError(operationCallCreateFolderV1, err) - } - - if response.IsError() { - return CreateFolderV1Response{}, NewAPIErrorWithResponse(operationCallCreateFolderV1, response, nil) - } - - return folderResponse, nil -} - -func CallDeleteFolderV1(httpClient *resty.Client, request DeleteFolderV1Request) (DeleteFolderV1Response, error) { - var folderResponse DeleteFolderV1Response - - httpRequest := httpClient. - R(). - SetResult(&folderResponse). - SetHeader("User-Agent", USER_AGENT). - SetBody(request) - - response, err := httpRequest.Delete(fmt.Sprintf("%v/v1/folders/%v", config.INFISICAL_URL, request.FolderName)) - if err != nil { - return DeleteFolderV1Response{}, NewGenericRequestError(operationCallDeleteFolderV1, err) - } - - if response.IsError() { - return DeleteFolderV1Response{}, NewAPIErrorWithResponse(operationCallDeleteFolderV1, response, nil) - } - - return folderResponse, nil -} - -func CallDeleteSecretsRawV3(httpClient *resty.Client, request DeleteSecretV3Request) error { - - var secretsResponse GetEncryptedSecretsV3Response - response, err := httpClient. - R(). - SetResult(&secretsResponse). - SetHeader("User-Agent", USER_AGENT). - SetBody(request). - Delete(fmt.Sprintf("%v/v3/secrets/raw/%s", config.INFISICAL_URL, request.SecretName)) - - if err != nil { - return NewGenericRequestError(operationCallDeleteSecretsV3, err) - } - - if response.IsError() { - additionalContext := "Please make sure your secret path, workspace and environment name are all correct." - return NewAPIErrorWithResponse(operationCallDeleteSecretsV3, response, &additionalContext) - } - - return nil -} - -func CallCreateServiceToken(httpClient *resty.Client, request CreateServiceTokenRequest) (CreateServiceTokenResponse, error) { - var createServiceTokenResponse CreateServiceTokenResponse - response, err := httpClient. - R(). - SetResult(&createServiceTokenResponse). - SetHeader("User-Agent", USER_AGENT). - SetBody(request). - Post(fmt.Sprintf("%v/v2/service-token/", config.INFISICAL_URL)) - - if err != nil { - return CreateServiceTokenResponse{}, NewGenericRequestError(operationCallCreateServiceToken, err) - } - - if response.IsError() { - return CreateServiceTokenResponse{}, NewAPIErrorWithResponse(operationCallCreateServiceToken, response, nil) - } - - return createServiceTokenResponse, nil -} - -func CallUniversalAuthLogin(httpClient *resty.Client, request UniversalAuthLoginRequest) (UniversalAuthLoginResponse, error) { - var universalAuthLoginResponse UniversalAuthLoginResponse - response, err := httpClient. - R(). - SetResult(&universalAuthLoginResponse). - SetHeader("User-Agent", USER_AGENT). - SetBody(request). - Post(fmt.Sprintf("%v/v1/auth/universal-auth/login/", config.INFISICAL_URL)) - - if err != nil { - return UniversalAuthLoginResponse{}, NewGenericRequestError(operationCallUniversalAuthLogin, err) - } - - if response.IsError() { - return UniversalAuthLoginResponse{}, NewAPIErrorWithResponse(operationCallUniversalAuthLogin, response, nil) - } - - return universalAuthLoginResponse, nil -} - -func CallMachineIdentityRefreshAccessToken(httpClient *resty.Client, request UniversalAuthRefreshRequest) (UniversalAuthRefreshResponse, error) { - var universalAuthRefreshResponse UniversalAuthRefreshResponse - response, err := httpClient. - R(). - SetResult(&universalAuthRefreshResponse). - SetHeader("User-Agent", USER_AGENT). - SetBody(request). - Post(fmt.Sprintf("%v/v1/auth/token/renew", config.INFISICAL_URL)) - - if err != nil { - return UniversalAuthRefreshResponse{}, NewGenericRequestError(operationCallMachineIdentityRefreshAccessToken, err) - } - - if response.IsError() { - return UniversalAuthRefreshResponse{}, NewAPIErrorWithResponse(operationCallMachineIdentityRefreshAccessToken, response, nil) - } - - return universalAuthRefreshResponse, nil -} - -func CallGetRawSecretsV3(httpClient *resty.Client, request GetRawSecretsV3Request) (GetRawSecretsV3Response, error) { - var getRawSecretsV3Response GetRawSecretsV3Response - req := httpClient. - R(). - SetResult(&getRawSecretsV3Response). - SetHeader("User-Agent", USER_AGENT). - SetBody(request). - SetQueryParam("workspaceId", request.WorkspaceId). - SetQueryParam("environment", request.Environment). - SetQueryParam("secretPath", request.SecretPath) - - if request.TagSlugs != "" { - req.SetQueryParam("tagSlugs", request.TagSlugs) - } - - if request.IncludeImport { - req.SetQueryParam("include_imports", "true") - } - if request.Recursive { - req.SetQueryParam("recursive", "true") - } - - if request.ExpandSecretReferences { - req.SetQueryParam("expandSecretReferences", "true") - } - - response, err := req.Get(fmt.Sprintf("%v/v3/secrets/raw", config.INFISICAL_URL)) - - if err != nil { - return GetRawSecretsV3Response{}, NewGenericRequestError(operationCallGetRawSecretsV3, err) - } - - if response.IsError() && - (strings.Contains(response.String(), "bot_not_found_error") || - strings.Contains(strings.ToLower(response.String()), "failed to find bot key") || - strings.Contains(strings.ToLower(response.String()), "bot is not active")) { - additionalContext := fmt.Sprintf(`Project with id %s is incompatible with your current CLI version. Upgrade your project by visiting the project settings page. If you're self-hosting and project upgrade option isn't yet available, contact your administrator to upgrade your Infisical instance to the latest release.`, request.WorkspaceId) - return GetRawSecretsV3Response{}, NewAPIErrorWithResponse(operationCallGetRawSecretsV3, response, &additionalContext) - } - - if response.IsError() { - return GetRawSecretsV3Response{}, NewAPIErrorWithResponse(operationCallGetRawSecretsV3, response, nil) - } - - getRawSecretsV3Response.ETag = response.Header().Get(("etag")) - - return getRawSecretsV3Response, nil -} - -func CallFetchSingleSecretByName(httpClient *resty.Client, request GetRawSecretV3ByNameRequest) (GetRawSecretV3ByNameResponse, error) { - var getRawSecretV3ByNameResponse GetRawSecretV3ByNameResponse - response, err := httpClient. - R(). - SetHeader("User-Agent", USER_AGENT). - SetResult(&getRawSecretV3ByNameResponse). - SetBody(request). - SetQueryParam("expandSecretReferences", "true"). - SetQueryParam("include_imports", "true"). - SetQueryParam("environment", request.Environment). - SetQueryParam("secretPath", request.SecretPath). - SetQueryParam("workspaceId", request.WorkspaceID). - SetQueryParam("type", "shared"). - Get(fmt.Sprintf("%v/v3/secrets/raw/%s", config.INFISICAL_URL, request.SecretName)) - - if err != nil { - return GetRawSecretV3ByNameResponse{}, NewGenericRequestError(operationCallFetchSingleSecretByName, err) - } - - if response.IsError() { - return GetRawSecretV3ByNameResponse{}, NewAPIErrorWithResponse(operationCallFetchSingleSecretByName, response, nil) - } - - getRawSecretV3ByNameResponse.ETag = response.Header().Get(("etag")) - - return getRawSecretV3ByNameResponse, nil -} - -func CallCreateDynamicSecretLeaseV1(httpClient *resty.Client, request CreateDynamicSecretLeaseV1Request) (CreateDynamicSecretLeaseV1Response, error) { - var createDynamicSecretLeaseResponse CreateDynamicSecretLeaseV1Response - response, err := httpClient. - R(). - SetResult(&createDynamicSecretLeaseResponse). - SetHeader("User-Agent", USER_AGENT). - SetBody(request). - Post(fmt.Sprintf("%v/v1/dynamic-secrets/leases", config.INFISICAL_URL)) - - if err != nil { - return CreateDynamicSecretLeaseV1Response{}, fmt.Errorf("CreateDynamicSecretLeaseV1: Unable to complete api request [err=%w]", err) - } - - if response.IsError() { - return CreateDynamicSecretLeaseV1Response{}, fmt.Errorf("CreateDynamicSecretLeaseV1: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String()) - } - - return createDynamicSecretLeaseResponse, nil -} - -func CallCreateRawSecretsV3(httpClient *resty.Client, request CreateRawSecretV3Request) error { - response, err := httpClient. - R(). - SetHeader("User-Agent", USER_AGENT). - SetBody(request). - Post(fmt.Sprintf("%v/v3/secrets/raw/%s", config.INFISICAL_URL, request.SecretName)) - - if err != nil { - return NewGenericRequestError(operationCallCreateRawSecretsV3, err) - } - - if response.IsError() { - return NewAPIErrorWithResponse(operationCallCreateRawSecretsV3, response, nil) - } - - return nil -} - -func CallUpdateRawSecretsV3(httpClient *resty.Client, request UpdateRawSecretByNameV3Request) error { - response, err := httpClient. - R(). - SetHeader("User-Agent", USER_AGENT). - SetBody(request). - Patch(fmt.Sprintf("%v/v3/secrets/raw/%s", config.INFISICAL_URL, request.SecretName)) - - if err != nil { - return NewGenericRequestError(operationCallUpdateRawSecretsV3, err) - } - - if response.IsError() { - return NewAPIErrorWithResponse(operationCallUpdateRawSecretsV3, response, nil) - } - - return nil -} - -func CallRegisterGatewayIdentityV1(httpClient *resty.Client) (*GetRelayCredentialsResponseV1, error) { - var resBody GetRelayCredentialsResponseV1 - response, err := httpClient. - R(). - SetResult(&resBody). - SetHeader("User-Agent", USER_AGENT). - Post(fmt.Sprintf("%v/v1/gateways/register-identity", config.INFISICAL_URL)) - - if err != nil { - return nil, NewGenericRequestError(operationCallRegisterGatewayIdentityV1, err) - } - - if response.IsError() { - return nil, NewAPIErrorWithResponse(operationCallRegisterGatewayIdentityV1, response, nil) - } - - return &resBody, nil -} - -func CallExchangeRelayCertV1(httpClient *resty.Client, request ExchangeRelayCertRequestV1) (*ExchangeRelayCertResponseV1, error) { - var resBody ExchangeRelayCertResponseV1 - response, err := httpClient. - R(). - SetResult(&resBody). - SetBody(request). - SetHeader("User-Agent", USER_AGENT). - Post(fmt.Sprintf("%v/v1/gateways/exchange-cert", config.INFISICAL_URL)) - - if err != nil { - return nil, NewGenericRequestError(operationCallExchangeRelayCertV1, err) - } - - if response.IsError() { - return nil, NewAPIErrorWithResponse(operationCallExchangeRelayCertV1, response, nil) - } - - return &resBody, nil -} - -func CallGatewayHeartBeatV1(httpClient *resty.Client) error { - response, err := httpClient. - R(). - SetHeader("User-Agent", USER_AGENT). - Post(fmt.Sprintf("%v/v1/gateways/heartbeat", config.INFISICAL_URL)) - - if err != nil { - return NewGenericRequestError(operationCallGatewayHeartBeatV1, err) - } - - if response.IsError() { - return NewAPIErrorWithResponse(operationCallGatewayHeartBeatV1, response, nil) - } - - return nil -} - -func CallBootstrapInstance(httpClient *resty.Client, request BootstrapInstanceRequest) (BootstrapInstanceResponse, error) { - var resBody BootstrapInstanceResponse - response, err := httpClient. - R(). - SetResult(&resBody). - SetHeader("User-Agent", USER_AGENT). - SetBody(request). - Post(fmt.Sprintf("%v/v1/admin/bootstrap", request.Domain)) - - if err != nil { - return BootstrapInstanceResponse{}, NewGenericRequestError(operationCallBootstrapInstance, err) - } - - if response.IsError() { - return BootstrapInstanceResponse{}, NewAPIErrorWithResponse(operationCallBootstrapInstance, response, nil) - } - - return resBody, nil -} diff --git a/cli/packages/api/errors.go b/cli/packages/api/errors.go deleted file mode 100644 index 4729d1264..000000000 --- a/cli/packages/api/errors.go +++ /dev/null @@ -1,80 +0,0 @@ -package api - -import ( - "fmt" - - "github.com/go-resty/resty/v2" - "github.com/infisical/go-sdk/packages/util" -) - -type GenericRequestError struct { - err error - operation string -} - -func (e *GenericRequestError) Error() string { - return fmt.Sprintf("%s: Unable to complete api request [err=%v]", e.operation, e.err) -} - -func NewGenericRequestError(operation string, err error) *GenericRequestError { - return &GenericRequestError{err: err, operation: operation} -} - -// APIError represents an error response from the API -type APIError struct { - AdditionalContext string `json:"additionalContext,omitempty"` - Operation string `json:"operation"` - Method string `json:"method"` - URL string `json:"url"` - StatusCode int `json:"statusCode"` - ErrorMessage string `json:"message,omitempty"` - ReqId string `json:"reqId,omitempty"` -} - -func (e *APIError) Error() string { - msg := fmt.Sprintf( - "%s Unsuccessful response [%v %v] [status-code=%v] [request-id=%v]", - e.Operation, - e.Method, - e.URL, - e.StatusCode, - e.ReqId, - ) - - if e.ErrorMessage != "" { - msg = fmt.Sprintf("%s [message=\"%s\"]", msg, e.ErrorMessage) - } - - if e.AdditionalContext != "" { - msg = fmt.Sprintf("%s [additional-context=\"%s\"]", msg, e.AdditionalContext) - } - - return msg -} - -func NewAPIErrorWithResponse(operation string, res *resty.Response, additionalContext *string) error { - errorMessage := util.TryParseErrorBody(res) - reqId := util.TryExtractReqId(res) - - if res == nil { - return NewGenericRequestError(operation, fmt.Errorf("response is nil")) - } - - apiError := &APIError{ - Operation: operation, - Method: res.Request.Method, - URL: res.Request.URL, - StatusCode: res.StatusCode(), - ReqId: reqId, - } - - if additionalContext != nil && *additionalContext != "" { - apiError.AdditionalContext = *additionalContext - } - - if errorMessage != "" { - apiError.ErrorMessage = errorMessage - } - - return apiError -} diff --git a/cli/packages/api/model.go b/cli/packages/api/model.go deleted file mode 100644 index 9bf666e44..000000000 --- a/cli/packages/api/model.go +++ /dev/null @@ -1,689 +0,0 @@ -package api - -import "time" - -// Stores info for login one -type LoginOneRequest struct { - Email string `json:"email"` - ClientPublicKey string `json:"clientPublicKey"` -} - -type LoginOneResponse struct { - ServerPublicKey string `json:"serverPublicKey"` - ServerSalt string `json:"salt"` -} - -// Stores info for login two - -type LoginTwoRequest struct { - Email string `json:"email"` - ClientProof string `json:"clientProof"` -} - -type LoginTwoResponse struct { - JTWToken string `json:"token"` - RefreshToken string `json:"refreshToken"` - PublicKey string `json:"publicKey"` - EncryptedPrivateKey string `json:"encryptedPrivateKey"` - IV string `json:"iv"` - Tag string `json:"tag"` -} - -type PullSecretsRequest struct { - Environment string `json:"environment"` -} - -type PullSecretsResponse struct { - Secrets []struct { - ID string `json:"_id"` - Workspace string `json:"workspace"` - Type string `json:"type"` - Environment string `json:"environment"` - SecretKeyCiphertext string `json:"secretKeyCiphertext"` - SecretKeyIV string `json:"secretKeyIV"` - SecretKeyTag string `json:"secretKeyTag"` - SecretKeyHash string `json:"secretKeyHash"` - SecretValueCiphertext string `json:"secretValueCiphertext"` - SecretValueIV string `json:"secretValueIV"` - SecretValueTag string `json:"secretValueTag"` - SecretValueHash string `json:"secretValueHash"` - V int `json:"__v"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` - User string `json:"user,omitempty"` - } `json:"secrets"` - Key struct { - ID string `json:"_id"` - EncryptedKey string `json:"encryptedKey"` - Nonce string `json:"nonce"` - Sender struct { - ID string `json:"_id"` - Email string `json:"email"` - CustomerID string `json:"customerId"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` - V int `json:"__v"` - FirstName string `json:"firstName"` - LastName string `json:"lastName"` - PublicKey string `json:"publicKey"` - } `json:"sender"` - Receiver string `json:"receiver"` - Workspace string `json:"workspace"` - V int `json:"__v"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` - } `json:"key"` -} - -type PullSecretsByInfisicalTokenResponse struct { - Secrets []struct { - ID string `json:"_id"` - Workspace string `json:"workspace"` - Type string `json:"type"` - Environment string `json:"environment"` - SecretKey struct { - Workspace string `json:"workspace"` - Ciphertext string `json:"ciphertext"` - Iv string `json:"iv"` - Tag string `json:"tag"` - Hash string `json:"hash"` - } `json:"secretKey"` - SecretValue struct { - Workspace string `json:"workspace"` - Ciphertext string `json:"ciphertext"` - Iv string `json:"iv"` - Tag string `json:"tag"` - Hash string `json:"hash"` - } `json:"secretValue"` - } `json:"secrets"` - Key struct { - EncryptedKey string `json:"encryptedKey"` - Nonce string `json:"nonce"` - Sender struct { - PublicKey string `json:"publicKey"` - } `json:"sender"` - Receiver struct { - RefreshVersion int `json:"refreshVersion"` - ID string `json:"_id"` - Email string `json:"email"` - CustomerID string `json:"customerId"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` - V int `json:"__v"` - FirstName string `json:"firstName"` - LastName string `json:"lastName"` - PublicKey string `json:"publicKey"` - } `json:"receiver"` - Workspace string `json:"workspace"` - } `json:"key"` -} - -type GetWorkSpacesResponse struct { - Workspaces []struct { - ID string `json:"_id"` - Name string `json:"name"` - Plan string `json:"plan,omitempty"` - V int `json:"__v"` - OrganizationId string `json:"orgId"` - } `json:"workspaces"` -} - -type GetProjectByIdResponse struct { - Project Project `json:"workspace"` -} - -type GetOrganizationsResponse struct { - Organizations []struct { - ID string `json:"id"` - Name string `json:"name"` - } `json:"organizations"` -} - -type SelectOrganizationResponse struct { - Token string `json:"token"` - MfaEnabled bool `json:"isMfaEnabled"` - MfaMethod string `json:"mfaMethod"` -} - -type SelectOrganizationRequest struct { - OrganizationId string `json:"organizationId"` -} - -type Secret struct { - SecretKeyCiphertext string `json:"secretKeyCiphertext,omitempty"` - SecretKeyIV string `json:"secretKeyIV,omitempty"` - SecretKeyTag string `json:"secretKeyTag,omitempty"` - SecretKeyHash string `json:"secretKeyHash,omitempty"` - SecretValueCiphertext string `json:"secretValueCiphertext,omitempty"` - SecretValueIV string `json:"secretValueIV,omitempty"` - SecretValueTag string `json:"secretValueTag,omitempty"` - SecretValueHash string `json:"secretValueHash,omitempty"` - SecretCommentCiphertext string `json:"secretCommentCiphertext,omitempty"` - SecretCommentIV string `json:"secretCommentIV,omitempty"` - SecretCommentTag string `json:"secretCommentTag,omitempty"` - SecretCommentHash string `json:"secretCommentHash,omitempty"` - Type string `json:"type,omitempty"` - ID string `json:"id,omitempty"` - PlainTextKey string `json:"plainTextKey"` -} - -type Project struct { - ID string `json:"id"` - Name string `json:"name"` - Slug string `json:"slug"` -} - -type RawSecret struct { - SecretKey string `json:"secretKey,omitempty"` - SecretValue string `json:"secretValue,omitempty"` - Type string `json:"type,omitempty"` - SecretComment string `json:"secretComment,omitempty"` - ID string `json:"id,omitempty"` -} - -type GetEncryptedWorkspaceKeyRequest struct { - WorkspaceId string `json:"workspaceId"` -} - -type GetEncryptedWorkspaceKeyResponse struct { - ID string `json:"_id"` - EncryptedKey string `json:"encryptedKey"` - Nonce string `json:"nonce"` - Sender struct { - ID string `json:"_id"` - Email string `json:"email"` - RefreshVersion int `json:"refreshVersion"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` - V int `json:"__v"` - FirstName string `json:"firstName"` - LastName string `json:"lastName"` - PublicKey string `json:"publicKey"` - } `json:"sender"` - Receiver string `json:"receiver"` - Workspace string `json:"workspace"` - V int `json:"__v"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` -} - -type GetSecretsByWorkspaceIdAndEnvironmentRequest struct { - EnvironmentName string `json:"environmentName"` - WorkspaceId string `json:"workspaceId"` -} - -type GetServiceTokenDetailsResponse struct { - ID string `json:"_id"` - Name string `json:"name"` - Workspace string `json:"workspace"` - ExpiresAt time.Time `json:"expiresAt"` - EncryptedKey string `json:"encryptedKey"` - Iv string `json:"iv"` - Tag string `json:"tag"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` - Scopes []struct { - Environment string `json:"environment"` - SecretPath string `json:"secretPath"` - } `json:"scopes"` -} - -type GetAccessibleEnvironmentsRequest struct { - WorkspaceId string `json:"workspaceId"` -} - -type GetAccessibleEnvironmentsResponse struct { - AccessibleEnvironments []struct { - Name string `json:"name"` - Slug string `json:"slug"` - IsWriteDenied bool `json:"isWriteDenied"` - } `json:"accessibleEnvironments"` -} - -type GetLoginOneV2Request struct { - Email string `json:"email"` - ClientPublicKey string `json:"clientPublicKey"` -} - -type GetLoginOneV2Response struct { - ServerPublicKey string `json:"serverPublicKey"` - Salt string `json:"salt"` -} - -type GetLoginTwoV2Request struct { - Email string `json:"email"` - ClientProof string `json:"clientProof"` - Password string `json:"password"` -} - -type GetLoginTwoV2Response struct { - MfaEnabled bool `json:"mfaEnabled"` - EncryptionVersion int `json:"encryptionVersion"` - Token string `json:"token"` - PublicKey string `json:"publicKey"` - EncryptedPrivateKey string `json:"encryptedPrivateKey"` - Iv string `json:"iv"` - Tag string `json:"tag"` - ProtectedKey string `json:"protectedKey"` - ProtectedKeyIV string `json:"protectedKeyIV"` - ProtectedKeyTag string `json:"protectedKeyTag"` - RefreshToken string `json:"RefreshToken"` -} - -type VerifyMfaTokenRequest struct { - Email string `json:"email"` - MFAToken string `json:"mfaToken"` - MFAMethod string `json:"mfaMethod"` -} - -type VerifyMfaTokenResponse struct { - EncryptionVersion int `json:"encryptionVersion"` - Token string `json:"token"` - PublicKey string `json:"publicKey"` - EncryptedPrivateKey string `json:"encryptedPrivateKey"` - Iv string `json:"iv"` - Tag string `json:"tag"` - ProtectedKey string `json:"protectedKey"` - ProtectedKeyIV string `json:"protectedKeyIV"` - ProtectedKeyTag string `json:"protectedKeyTag"` - RefreshToken string `json:"refreshToken"` -} - -type VerifyMfaTokenErrorResponse struct { - Type string `json:"type"` - Message string `json:"message"` - Context struct { - Code string `json:"code"` - TriesLeft int `json:"triesLeft"` - } `json:"context"` - Level int `json:"level"` - LevelName string `json:"level_name"` - StatusCode int `json:"status_code"` - DatetimeIso time.Time `json:"datetime_iso"` - Application string `json:"application"` - Extra []interface{} `json:"extra"` -} - -type GetNewAccessTokenWithRefreshTokenResponse struct { - Token string `json:"token"` -} - -type GetEncryptedSecretsV3Request struct { - Environment string `json:"environment"` - WorkspaceId string `json:"workspaceId"` - SecretPath string `json:"secretPath"` - IncludeImport bool `json:"include_imports"` - Recursive bool `json:"recursive"` -} - -type GetFoldersV1Request struct { - Environment string `json:"environment"` - WorkspaceId string `json:"workspaceId"` - FoldersPath string `json:"foldersPath"` -} - -type GetFoldersV1Response struct { - Folders []struct { - ID string `json:"id"` - Name string `json:"name"` - } `json:"folders"` -} - -type CreateFolderV1Request struct { - FolderName string `json:"name"` - WorkspaceId string `json:"workspaceId"` - Environment string `json:"environment"` - Path string `json:"path"` -} - -type CreateFolderV1Response struct { - Folder struct { - ID string `json:"id"` - Name string `json:"name"` - } `json:"folder"` -} - -type DeleteFolderV1Request struct { - FolderName string `json:"folderName"` - WorkspaceId string `json:"workspaceId"` - Environment string `json:"environment"` - Directory string `json:"directory"` -} - -type DeleteFolderV1Response struct { - Folders []struct { - ID string `json:"id"` - Name string `json:"name"` - } `json:"folders"` -} - -type EncryptedSecretV3 struct { - ID string `json:"_id"` - Version int `json:"version"` - Workspace string `json:"workspace"` - Type string `json:"type"` - Tags []struct { - ID string `json:"_id"` - Name string `json:"name"` - Slug string `json:"slug"` - Workspace string `json:"workspace"` - } `json:"tags"` - Environment string `json:"environment"` - SecretKeyCiphertext string `json:"secretKeyCiphertext"` - SecretKeyIV string `json:"secretKeyIV"` - SecretKeyTag string `json:"secretKeyTag"` - SecretValueCiphertext string `json:"secretValueCiphertext"` - SecretValueIV string `json:"secretValueIV"` - SecretValueTag string `json:"secretValueTag"` - SecretCommentCiphertext string `json:"secretCommentCiphertext"` - SecretCommentIV string `json:"secretCommentIV"` - SecretCommentTag string `json:"secretCommentTag"` - Algorithm string `json:"algorithm"` - KeyEncoding string `json:"keyEncoding"` - Folder string `json:"folder"` - V int `json:"__v"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` -} - -type ImportedSecretV3 struct { - Environment string `json:"environment"` - FolderId string `json:"folderId"` - SecretPath string `json:"secretPath"` - Secrets []EncryptedSecretV3 `json:"secrets"` -} - -type ImportedRawSecretV3 struct { - SecretPath string `json:"secretPath"` - Environment string `json:"environment"` - FolderId string `json:"folderId"` - Secrets []struct { - ID string `json:"id"` - Workspace string `json:"workspace"` - Environment string `json:"environment"` - Version int `json:"version"` - Type string `json:"type"` - SecretKey string `json:"secretKey"` - SecretValue string `json:"secretValue"` - SecretComment string `json:"secretComment"` - } `json:"secrets"` -} - -type GetEncryptedSecretsV3Response struct { - Secrets []EncryptedSecretV3 `json:"secrets"` - ImportedSecrets []ImportedSecretV3 `json:"imports,omitempty"` -} - -type CreateSecretV3Request struct { - SecretName string `json:"secretName"` - WorkspaceID string `json:"workspaceId"` - Type string `json:"type"` - Environment string `json:"environment"` - SecretKeyCiphertext string `json:"secretKeyCiphertext"` - SecretKeyIV string `json:"secretKeyIV"` - SecretKeyTag string `json:"secretKeyTag"` - SecretValueCiphertext string `json:"secretValueCiphertext"` - SecretValueIV string `json:"secretValueIV"` - SecretValueTag string `json:"secretValueTag"` - SecretCommentCiphertext string `json:"secretCommentCiphertext"` - SecretCommentIV string `json:"secretCommentIV"` - SecretCommentTag string `json:"secretCommentTag"` - SecretPath string `json:"secretPath"` -} - -type CreateRawSecretV3Request struct { - SecretName string `json:"-"` - WorkspaceID string `json:"workspaceId"` - Type string `json:"type,omitempty"` - Environment string `json:"environment"` - SecretPath string `json:"secretPath,omitempty"` - SecretValue string `json:"secretValue"` - SecretComment string `json:"secretComment,omitempty"` - SkipMultilineEncoding bool `json:"skipMultilineEncoding,omitempty"` -} - -type DeleteSecretV3Request struct { - SecretName string `json:"secretName"` - WorkspaceId string `json:"workspaceId"` - Environment string `json:"environment"` - Type string `json:"type,omitempty"` - SecretPath string `json:"secretPath,omitempty"` -} - -type UpdateSecretByNameV3Request struct { - WorkspaceID string `json:"workspaceId"` - Environment string `json:"environment"` - Type string `json:"type"` - SecretPath string `json:"secretPath"` - SecretValueCiphertext string `json:"secretValueCiphertext"` - SecretValueIV string `json:"secretValueIV"` - SecretValueTag string `json:"secretValueTag"` -} - -type UpdateRawSecretByNameV3Request struct { - SecretName string `json:"-"` - WorkspaceID string `json:"workspaceId"` - Environment string `json:"environment"` - SecretPath string `json:"secretPath,omitempty"` - SecretValue string `json:"secretValue"` - Type string `json:"type,omitempty"` -} - -type GetSingleSecretByNameV3Request struct { - SecretName string `json:"secretName"` - WorkspaceId string `json:"workspaceId"` - Environment string `json:"environment"` - Type string `json:"type"` - SecretPath string `json:"secretPath"` -} - -type GetSingleSecretByNameSecretResponse struct { - Secrets []struct { - ID string `json:"_id"` - Version int `json:"version"` - Workspace string `json:"workspace"` - Type string `json:"type"` - Environment string `json:"environment"` - SecretKeyCiphertext string `json:"secretKeyCiphertext"` - SecretKeyIV string `json:"secretKeyIV"` - SecretKeyTag string `json:"secretKeyTag"` - SecretValueCiphertext string `json:"secretValueCiphertext"` - SecretValueIV string `json:"secretValueIV"` - SecretValueTag string `json:"secretValueTag"` - SecretCommentCiphertext string `json:"secretCommentCiphertext"` - SecretCommentIV string `json:"secretCommentIV"` - SecretCommentTag string `json:"secretCommentTag"` - Algorithm string `json:"algorithm"` - KeyEncoding string `json:"keyEncoding"` - Folder string `json:"folder"` - V int `json:"__v"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` - } `json:"secrets"` -} - -type ScopePermission struct { - Environment string `json:"environment"` - SecretPath string `json:"secretPath"` -} - -type CreateServiceTokenRequest struct { - Name string `json:"name"` - WorkspaceId string `json:"workspaceId"` - Scopes []ScopePermission `json:"scopes"` - ExpiresIn int `json:"expiresIn"` - EncryptedKey string `json:"encryptedKey"` - Iv string `json:"iv"` - Tag string `json:"tag"` - RandomBytes string `json:"randomBytes"` - Permissions []string `json:"permissions"` -} - -type ServiceTokenData struct { - ID string `json:"_id"` - Name string `json:"name"` - Workspace string `json:"workspace"` - Scopes []interface{} `json:"scopes"` - User string `json:"user"` - LastUsed time.Time `json:"lastUsed"` - Permissions []string `json:"permissions"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` -} - -type CreateServiceTokenResponse struct { - ServiceToken string `json:"serviceToken"` - ServiceTokenData ServiceTokenData `json:"serviceTokenData"` -} - -type UniversalAuthLoginRequest struct { - ClientSecret string `json:"clientSecret"` - ClientId string `json:"clientId"` -} - -type UniversalAuthLoginResponse struct { - AccessToken string `json:"accessToken"` - AccessTokenTTL int `json:"expiresIn"` - TokenType string `json:"tokenType"` - AccessTokenMaxTTL int `json:"accessTokenMaxTTL"` -} - -type UniversalAuthRefreshRequest struct { - AccessToken string `json:"accessToken"` -} - -type UniversalAuthRefreshResponse struct { - AccessToken string `json:"accessToken"` - AccessTokenTTL int `json:"expiresIn"` - TokenType string `json:"tokenType"` - AccessTokenMaxTTL int `json:"accessTokenMaxTTL"` -} - -type CreateDynamicSecretLeaseV1Request struct { - Environment string `json:"environment"` - ProjectSlug string `json:"projectSlug"` - SecretPath string `json:"secretPath,omitempty"` - Slug string `json:"slug"` - TTL string `json:"ttl,omitempty"` -} - -type CreateDynamicSecretLeaseV1Response struct { - Lease struct { - Id string `json:"id"` - ExpireAt time.Time `json:"expireAt"` - } `json:"lease"` - DynamicSecret struct { - Id string `json:"id"` - DefaultTTL string `json:"defaultTTL"` - MaxTTL string `json:"maxTTL"` - Type string `json:"type"` - } `json:"dynamicSecret"` - Data map[string]interface{} `json:"data"` -} - -type GetRawSecretsV3Request struct { - Environment string `json:"environment"` - WorkspaceId string `json:"workspaceId"` - SecretPath string `json:"secretPath"` - IncludeImport bool `json:"include_imports"` - Recursive bool `json:"recursive"` - TagSlugs string `json:"tagSlugs,omitempty"` - ExpandSecretReferences bool `json:"expandSecretReferences,omitempty"` -} - -type GetRawSecretsV3Response struct { - Secrets []struct { - ID string `json:"_id"` - Version int `json:"version"` - Workspace string `json:"workspace"` - Type string `json:"type"` - Environment string `json:"environment"` - SecretKey string `json:"secretKey"` - SecretValue string `json:"secretValue"` - SecretComment string `json:"secretComment"` - SecretPath string `json:"secretPath"` - } `json:"secrets"` - Imports []ImportedRawSecretV3 `json:"imports"` - ETag string -} - -type GetRawSecretV3ByNameRequest struct { - SecretName string `json:"secretName"` - WorkspaceID string `json:"workspaceId"` - Type string `json:"type,omitempty"` - Environment string `json:"environment"` - SecretPath string `json:"secretPath,omitempty"` -} - -type GetRawSecretV3ByNameResponse struct { - Secret struct { - ID string `json:"_id"` - Version int `json:"version"` - Workspace string `json:"workspace"` - Type string `json:"type"` - Environment string `json:"environment"` - SecretKey string `json:"secretKey"` - SecretValue string `json:"secretValue"` - SecretComment string `json:"secretComment"` - SecretPath string `json:"secretPath"` - } `json:"secret"` - ETag string -} - -type GetRelayCredentialsResponseV1 struct { - TurnServerUsername string `json:"turnServerUsername"` - TurnServerPassword string `json:"turnServerPassword"` - TurnServerRealm string `json:"turnServerRealm"` - TurnServerAddress string `json:"turnServerAddress"` - InfisicalStaticIp string `json:"infisicalStaticIp"` -} - -type ExchangeRelayCertRequestV1 struct { - RelayAddress string `json:"relayAddress"` -} - -type ExchangeRelayCertResponseV1 struct { - SerialNumber string `json:"serialNumber"` - PrivateKey string `json:"privateKey"` - Certificate string `json:"certificate"` - CertificateChain string `json:"certificateChain"` -} - -type BootstrapInstanceRequest struct { - Email string `json:"email"` - Password string `json:"password"` - Organization string `json:"organization"` - Domain string `json:"domain"` -} - -type BootstrapInstanceResponse struct { - Message string `json:"message"` - Identity BootstrapIdentity `json:"identity"` - Organization BootstrapOrganization `json:"organization"` - User BootstrapUser `json:"user"` -} - -type BootstrapIdentity struct { - ID string `json:"id"` - Name string `json:"name"` - Credentials BootstrapIdentityCredentials `json:"credentials"` -} - -type BootstrapIdentityCredentials struct { - Token string `json:"token"` -} - -type BootstrapOrganization struct { - ID string `json:"id"` - Name string `json:"name"` - Slug string `json:"slug"` -} - -type BootstrapUser struct { - ID string `json:"id"` - Email string `json:"email"` - FirstName string `json:"firstName"` - LastName string `json:"lastName"` - Username string `json:"username"` - SuperAdmin bool `json:"superAdmin"` -} diff --git a/cli/packages/cmd/agent.go b/cli/packages/cmd/agent.go deleted file mode 100644 index cb10050dd..000000000 --- a/cli/packages/cmd/agent.go +++ /dev/null @@ -1,1081 +0,0 @@ -/* -Copyright (c) 2023 Infisical Inc. -*/ -package cmd - -import ( - "bytes" - "context" - "encoding/base64" - "encoding/json" - "fmt" - "io/ioutil" - "os" - "os/exec" - "os/signal" - "path" - "runtime" - "slices" - "sync" - "syscall" - "text/template" - "time" - - infisicalSdk "github.com/infisical/go-sdk" - "github.com/rs/zerolog/log" - "gopkg.in/yaml.v2" - - "github.com/Infisical/infisical-merge/packages/api" - "github.com/Infisical/infisical-merge/packages/config" - "github.com/Infisical/infisical-merge/packages/models" - "github.com/Infisical/infisical-merge/packages/util" - "github.com/spf13/cobra" -) - -const DEFAULT_INFISICAL_CLOUD_URL = "https://app.infisical.com" - -// duration to reduce from expiry of dynamic leases so that it gets triggered before expiry -const DYNAMIC_SECRET_PRUNE_EXPIRE_BUFFER = -15 - -type Config struct { - Infisical InfisicalConfig `yaml:"infisical"` - Auth AuthConfig `yaml:"auth"` - Sinks []Sink `yaml:"sinks"` - Templates []Template `yaml:"templates"` -} - -type InfisicalConfig struct { - Address string `yaml:"address"` - ExitAfterAuth bool `yaml:"exit-after-auth"` -} - -type AuthConfig struct { - Type string `yaml:"type"` - Config interface{} `yaml:"config"` -} - -type UniversalAuth struct { - ClientIDPath string `yaml:"client-id"` - ClientSecretPath string `yaml:"client-secret"` - RemoveClientSecretOnRead bool `yaml:"remove_client_secret_on_read"` -} - -type KubernetesAuth struct { - IdentityID string `yaml:"identity-id"` - ServiceAccountToken string `yaml:"service-account-token"` -} - -type AzureAuth struct { - IdentityID string `yaml:"identity-id"` -} - -type GcpIdTokenAuth struct { - IdentityID string `yaml:"identity-id"` -} - -type GcpIamAuth struct { - IdentityID string `yaml:"identity-id"` - ServiceAccountKey string `yaml:"service-account-key"` -} - -type AwsIamAuth struct { - IdentityID string `yaml:"identity-id"` -} - -type Sink struct { - Type string `yaml:"type"` - Config SinkDetails `yaml:"config"` -} - -type SinkDetails struct { - Path string `yaml:"path"` -} - -type Template struct { - SourcePath string `yaml:"source-path"` - Base64TemplateContent string `yaml:"base64-template-content"` - DestinationPath string `yaml:"destination-path"` - TemplateContent string `yaml:"template-content"` - - Config struct { // Configurations for the template - PollingInterval string `yaml:"polling-interval"` // How often to poll for changes in the secret - Execute struct { - Command string `yaml:"command"` // Command to execute once the template has been rendered - Timeout int64 `yaml:"timeout"` // Timeout for the command - } `yaml:"execute"` // Command to execute once the template has been rendered - } `yaml:"config"` -} - -type DynamicSecretLease struct { - LeaseID string - ExpireAt time.Time - Environment string - SecretPath string - Slug string - ProjectSlug string - Data map[string]interface{} - TemplateIDs []int -} - -type DynamicSecretLeaseManager struct { - leases []DynamicSecretLease - mutex sync.Mutex -} - -func (d *DynamicSecretLeaseManager) Prune() { - d.mutex.Lock() - defer d.mutex.Unlock() - - d.leases = slices.DeleteFunc(d.leases, func(s DynamicSecretLease) bool { - return time.Now().After(s.ExpireAt.Add(DYNAMIC_SECRET_PRUNE_EXPIRE_BUFFER * time.Second)) - }) -} - -func (d *DynamicSecretLeaseManager) Append(lease DynamicSecretLease) { - d.mutex.Lock() - defer d.mutex.Unlock() - - index := slices.IndexFunc(d.leases, func(s DynamicSecretLease) bool { - if lease.SecretPath == s.SecretPath && lease.Environment == s.Environment && lease.ProjectSlug == s.ProjectSlug && lease.Slug == s.Slug { - return true - } - return false - }) - - if index != -1 { - d.leases[index].TemplateIDs = append(d.leases[index].TemplateIDs, lease.TemplateIDs...) - return - } - d.leases = append(d.leases, lease) -} - -func (d *DynamicSecretLeaseManager) RegisterTemplate(projectSlug, environment, secretPath, slug string, templateId int) { - d.mutex.Lock() - defer d.mutex.Unlock() - - index := slices.IndexFunc(d.leases, func(lease DynamicSecretLease) bool { - if lease.SecretPath == secretPath && lease.Environment == environment && lease.ProjectSlug == projectSlug && lease.Slug == slug { - return true - } - return false - }) - - if index != -1 { - d.leases[index].TemplateIDs = append(d.leases[index].TemplateIDs, templateId) - } -} - -func (d *DynamicSecretLeaseManager) GetLease(projectSlug, environment, secretPath, slug string) *DynamicSecretLease { - d.mutex.Lock() - defer d.mutex.Unlock() - - for _, lease := range d.leases { - if lease.SecretPath == secretPath && lease.Environment == environment && lease.ProjectSlug == projectSlug && lease.Slug == slug { - return &lease - } - } - - return nil -} - -// for a given template find the first expiring lease -// The bool indicates whether it contains valid expiry list -func (d *DynamicSecretLeaseManager) GetFirstExpiringLeaseTime(templateId int) (time.Time, bool) { - d.mutex.Lock() - defer d.mutex.Unlock() - - if len(d.leases) == 0 { - return time.Time{}, false - } - - var firstExpiry time.Time - for i, el := range d.leases { - if i == 0 { - firstExpiry = el.ExpireAt - } - newLeaseTime := el.ExpireAt.Add(DYNAMIC_SECRET_PRUNE_EXPIRE_BUFFER * time.Second) - if newLeaseTime.Before(firstExpiry) { - firstExpiry = newLeaseTime - } - } - return firstExpiry, true -} - -func NewDynamicSecretLeaseManager(sigChan chan os.Signal) *DynamicSecretLeaseManager { - manager := &DynamicSecretLeaseManager{} - return manager -} - -func ReadFile(filePath string) ([]byte, error) { - return ioutil.ReadFile(filePath) -} - -func ExecuteCommandWithTimeout(command string, timeout int64) error { - - shell := [2]string{"sh", "-c"} - if runtime.GOOS == "windows" { - shell = [2]string{"cmd", "/C"} - } else { - currentShell := os.Getenv("SHELL") - if currentShell != "" { - shell[0] = currentShell - } - } - - ctx := context.Background() - if timeout > 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second) - defer cancel() - } - - cmd := exec.CommandContext(ctx, shell[0], shell[1], command) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - if err := cmd.Run(); err != nil { - if exitError, ok := err.(*exec.ExitError); ok { // type assertion - if exitError.ProcessState.ExitCode() == -1 { - return fmt.Errorf("command timed out") - } - } - return err - } else { - return nil - } -} - -func FileExists(filepath string) bool { - info, err := os.Stat(filepath) - if os.IsNotExist(err) { - return false - } - return !info.IsDir() -} - -// WriteToFile writes data to the specified file path. -func WriteBytesToFile(data *bytes.Buffer, outputPath string) error { - outputFile, err := os.Create(outputPath) - if err != nil { - return err - } - defer outputFile.Close() - - _, err = outputFile.Write(data.Bytes()) - return err -} - -func ParseAuthConfig(authConfigFile []byte, destination interface{}) error { - if err := yaml.Unmarshal(authConfigFile, destination); err != nil { - return err - } - - return nil -} - -func ParseAgentConfig(configFile []byte) (*Config, error) { - var rawConfig struct { - Infisical InfisicalConfig `yaml:"infisical"` - Auth struct { - Type string `yaml:"type"` - Config map[string]interface{} `yaml:"config"` - } `yaml:"auth"` - Sinks []Sink `yaml:"sinks"` - Templates []Template `yaml:"templates"` - } - - if err := yaml.Unmarshal(configFile, &rawConfig); err != nil { - return nil, err - } - - // Set defaults - if rawConfig.Infisical.Address == "" { - rawConfig.Infisical.Address = DEFAULT_INFISICAL_CLOUD_URL - } - - config.INFISICAL_URL = util.AppendAPIEndpoint(rawConfig.Infisical.Address) - - log.Info().Msgf("Infisical instance address set to %s", rawConfig.Infisical.Address) - - config := &Config{ - Infisical: rawConfig.Infisical, - Auth: AuthConfig{ - Type: rawConfig.Auth.Type, - Config: rawConfig.Auth.Config, - }, - Sinks: rawConfig.Sinks, - Templates: rawConfig.Templates, - } - - return config, nil -} - -type secretArguments struct { - IsRecursive bool `json:"recursive"` - ShouldExpandSecretReferences *bool `json:"expandSecretReferences,omitempty"` -} - -func (s *secretArguments) SetDefaults() { - if s.ShouldExpandSecretReferences == nil { - var bool = true - s.ShouldExpandSecretReferences = &bool - } -} - -func secretTemplateFunction(accessToken string, existingEtag string, currentEtag *string) func(string, string, string, ...string) ([]models.SingleEnvironmentVariable, error) { - // ...string is because golang doesn't have optional arguments. - // thus we make it slice and pick it only first element - return func(projectID, envSlug, secretPath string, args ...string) ([]models.SingleEnvironmentVariable, error) { - var parsedArguments secretArguments - // to make it optional - if len(args) > 0 { - err := json.Unmarshal([]byte(args[0]), &parsedArguments) - if err != nil { - return nil, err - } - } - - parsedArguments.SetDefaults() - - res, err := util.GetPlainTextSecretsV3(accessToken, projectID, envSlug, secretPath, true, parsedArguments.IsRecursive, "", *parsedArguments.ShouldExpandSecretReferences) - if err != nil { - return nil, err - } - - if existingEtag != res.Etag { - *currentEtag = res.Etag - } - - return res.Secrets, nil - } -} - -func getSingleSecretTemplateFunction(accessToken string, existingEtag string, currentEtag *string) func(string, string, string, string) (models.SingleEnvironmentVariable, error) { - return func(projectID, envSlug, secretPath, secretName string) (models.SingleEnvironmentVariable, error) { - secret, requestEtag, err := util.GetSinglePlainTextSecretByNameV3(accessToken, projectID, envSlug, secretPath, secretName) - if err != nil { - return models.SingleEnvironmentVariable{}, err - } - - if existingEtag != requestEtag { - *currentEtag = requestEtag - } - - return secret, nil - } -} - -func dynamicSecretTemplateFunction(accessToken string, dynamicSecretManager *DynamicSecretLeaseManager, templateId int) func(...string) (map[string]interface{}, error) { - return func(args ...string) (map[string]interface{}, error) { - argLength := len(args) - if argLength != 4 && argLength != 5 { - return nil, fmt.Errorf("invalid arguments found for dynamic-secret function. Check template %d", templateId) - } - - projectSlug, envSlug, secretPath, slug, ttl := args[0], args[1], args[2], args[3], "" - if argLength == 5 { - ttl = args[4] - } - dynamicSecretData := dynamicSecretManager.GetLease(projectSlug, envSlug, secretPath, slug) - if dynamicSecretData != nil { - dynamicSecretManager.RegisterTemplate(projectSlug, envSlug, secretPath, slug, templateId) - return dynamicSecretData.Data, nil - } - - res, err := util.CreateDynamicSecretLease(accessToken, projectSlug, envSlug, secretPath, slug, ttl) - if err != nil { - return nil, err - } - - dynamicSecretManager.Append(DynamicSecretLease{LeaseID: res.Lease.Id, ExpireAt: res.Lease.ExpireAt, Environment: envSlug, SecretPath: secretPath, Slug: slug, ProjectSlug: projectSlug, Data: res.Data, TemplateIDs: []int{templateId}}) - return res.Data, nil - } -} - -func ProcessTemplate(templateId int, templatePath string, data interface{}, accessToken string, existingEtag string, currentEtag *string, dynamicSecretManager *DynamicSecretLeaseManager) (*bytes.Buffer, error) { - // custom template function to fetch secrets from Infisical - secretFunction := secretTemplateFunction(accessToken, existingEtag, currentEtag) - dynamicSecretFunction := dynamicSecretTemplateFunction(accessToken, dynamicSecretManager, templateId) - getSingleSecretFunction := getSingleSecretTemplateFunction(accessToken, existingEtag, currentEtag) - funcs := template.FuncMap{ - "secret": secretFunction, // depreciated - "listSecrets": secretFunction, - "dynamic_secret": dynamicSecretFunction, - "getSecretByName": getSingleSecretFunction, - "minus": func(a, b int) int { - return a - b - }, - "add": func(a, b int) int { - return a + b - }, - } - - templateName := path.Base(templatePath) - tmpl, err := template.New(templateName).Funcs(funcs).ParseFiles(templatePath) - if err != nil { - return nil, err - } - - var buf bytes.Buffer - if err := tmpl.Execute(&buf, data); err != nil { - return nil, err - } - - return &buf, nil -} - -func ProcessBase64Template(templateId int, encodedTemplate string, data interface{}, accessToken string, existingEtag string, currentEtag *string, dynamicSecretLeaser *DynamicSecretLeaseManager) (*bytes.Buffer, error) { - // custom template function to fetch secrets from Infisical - decoded, err := base64.StdEncoding.DecodeString(encodedTemplate) - if err != nil { - return nil, err - } - - templateString := string(decoded) - - secretFunction := secretTemplateFunction(accessToken, existingEtag, currentEtag) // TODO: Fix this - dynamicSecretFunction := dynamicSecretTemplateFunction(accessToken, dynamicSecretLeaser, templateId) - funcs := template.FuncMap{ - "secret": secretFunction, - "dynamic_secret": dynamicSecretFunction, - } - - templateName := "base64Template" - - tmpl, err := template.New(templateName).Funcs(funcs).Parse(templateString) - if err != nil { - return nil, err - } - - var buf bytes.Buffer - if err := tmpl.Execute(&buf, data); err != nil { - return nil, err - } - - return &buf, nil -} - -func ProcessLiteralTemplate(templateId int, templateString string, data interface{}, accessToken string, existingEtag string, currentEtag *string, dynamicSecretLeaser *DynamicSecretLeaseManager) (*bytes.Buffer, error) { - secretFunction := secretTemplateFunction(accessToken, existingEtag, currentEtag) // TODO: Fix this - dynamicSecretFunction := dynamicSecretTemplateFunction(accessToken, dynamicSecretLeaser, templateId) - funcs := template.FuncMap{ - "secret": secretFunction, - "dynamic_secret": dynamicSecretFunction, - } - - templateName := "literalTemplate" - - tmpl, err := template.New(templateName).Funcs(funcs).Parse(templateString) - if err != nil { - return nil, err - } - - var buf bytes.Buffer - if err := tmpl.Execute(&buf, data); err != nil { - return nil, err - } - - return &buf, nil -} - -type AgentManager struct { - accessToken string - accessTokenTTL time.Duration - accessTokenMaxTTL time.Duration - accessTokenFetchedTime time.Time - accessTokenRefreshedTime time.Time - mutex sync.Mutex - filePaths []Sink // Store file paths if needed - templates []Template - dynamicSecretLeases *DynamicSecretLeaseManager - - authConfigBytes []byte - authStrategy util.AuthStrategyType - - newAccessTokenNotificationChan chan bool - removeUniversalAuthClientSecretOnRead bool - cachedUniversalAuthClientSecret string - exitAfterAuth bool - - infisicalClient infisicalSdk.InfisicalClientInterface -} - -type NewAgentMangerOptions struct { - FileDeposits []Sink - Templates []Template - - AuthConfigBytes []byte - AuthStrategy util.AuthStrategyType - - NewAccessTokenNotificationChan chan bool - ExitAfterAuth bool -} - -func NewAgentManager(options NewAgentMangerOptions) *AgentManager { - customHeaders, err := util.GetInfisicalCustomHeadersMap() - if err != nil { - util.HandleError(err, "Unable to get custom headers") - } - return &AgentManager{ - filePaths: options.FileDeposits, - templates: options.Templates, - - authConfigBytes: options.AuthConfigBytes, - authStrategy: options.AuthStrategy, - - newAccessTokenNotificationChan: options.NewAccessTokenNotificationChan, - exitAfterAuth: options.ExitAfterAuth, - - infisicalClient: infisicalSdk.NewInfisicalClient(context.Background(), infisicalSdk.Config{ - SiteUrl: config.INFISICAL_URL, - UserAgent: api.USER_AGENT, // ? Should we perhaps use a different user agent for the Agent for better analytics? - AutoTokenRefresh: false, - CustomHeaders: customHeaders, - }), - } - -} - -func (tm *AgentManager) SetToken(token string, accessTokenTTL time.Duration, accessTokenMaxTTL time.Duration) { - tm.mutex.Lock() - defer tm.mutex.Unlock() - - tm.accessToken = token - tm.accessTokenTTL = accessTokenTTL - tm.accessTokenMaxTTL = accessTokenMaxTTL - - tm.newAccessTokenNotificationChan <- true -} - -func (tm *AgentManager) GetToken() string { - tm.mutex.Lock() - defer tm.mutex.Unlock() - - return tm.accessToken -} - -func (tm *AgentManager) FetchUniversalAuthAccessToken() (credential infisicalSdk.MachineIdentityCredential, e error) { - - var universalAuthConfig UniversalAuth - if err := ParseAuthConfig(tm.authConfigBytes, &universalAuthConfig); err != nil { - return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to parse auth config due to error: %v", err) - } - - clientID, err := util.GetEnvVarOrFileContent(util.INFISICAL_UNIVERSAL_AUTH_CLIENT_ID_NAME, universalAuthConfig.ClientIDPath) - if err != nil { - return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to get client id: %v", err) - } - - clientSecret, err := util.GetEnvVarOrFileContent("INFISICAL_UNIVERSAL_CLIENT_SECRET", universalAuthConfig.ClientSecretPath) - if err != nil { - if len(tm.cachedUniversalAuthClientSecret) == 0 { - return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to get client secret: %v", err) - } - clientSecret = tm.cachedUniversalAuthClientSecret - } - - tm.cachedUniversalAuthClientSecret = clientSecret - if universalAuthConfig.RemoveClientSecretOnRead { - defer os.Remove(universalAuthConfig.ClientSecretPath) - } - - return tm.infisicalClient.Auth().UniversalAuthLogin(clientID, clientSecret) - -} - -func (tm *AgentManager) FetchKubernetesAuthAccessToken() (credential infisicalSdk.MachineIdentityCredential, err error) { - - var kubernetesAuthConfig KubernetesAuth - if err := ParseAuthConfig(tm.authConfigBytes, &kubernetesAuthConfig); err != nil { - return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to parse auth config due to error: %v", err) - } - - identityId, err := util.GetEnvVarOrFileContent(util.INFISICAL_MACHINE_IDENTITY_ID_NAME, kubernetesAuthConfig.IdentityID) - if err != nil { - return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to get identity id: %v", err) - } - - serviceAccountTokenPath := os.Getenv(util.INFISICAL_KUBERNETES_SERVICE_ACCOUNT_TOKEN_NAME) - if serviceAccountTokenPath == "" { - serviceAccountTokenPath = kubernetesAuthConfig.ServiceAccountToken - if serviceAccountTokenPath == "" { - serviceAccountTokenPath = "/var/run/secrets/kubernetes.io/serviceaccount/token" - } - } - - return tm.infisicalClient.Auth().KubernetesAuthLogin(identityId, serviceAccountTokenPath) - -} - -func (tm *AgentManager) FetchAzureAuthAccessToken() (credential infisicalSdk.MachineIdentityCredential, err error) { - - var azureAuthConfig AzureAuth - if err := ParseAuthConfig(tm.authConfigBytes, &azureAuthConfig); err != nil { - return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to parse auth config due to error: %v", err) - } - - identityId, err := util.GetEnvVarOrFileContent(util.INFISICAL_MACHINE_IDENTITY_ID_NAME, azureAuthConfig.IdentityID) - if err != nil { - return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to get identity id: %v", err) - } - - return tm.infisicalClient.Auth().AzureAuthLogin(identityId, "") - -} - -func (tm *AgentManager) FetchGcpIdTokenAuthAccessToken() (credential infisicalSdk.MachineIdentityCredential, err error) { - - var gcpIdTokenAuthConfig GcpIdTokenAuth - if err := ParseAuthConfig(tm.authConfigBytes, &gcpIdTokenAuthConfig); err != nil { - return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to parse auth config due to error: %v", err) - } - - identityId, err := util.GetEnvVarOrFileContent(util.INFISICAL_MACHINE_IDENTITY_ID_NAME, gcpIdTokenAuthConfig.IdentityID) - if err != nil { - return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to get identity id: %v", err) - } - - return tm.infisicalClient.Auth().GcpIdTokenAuthLogin(identityId) - -} - -func (tm *AgentManager) FetchGcpIamAuthAccessToken() (credential infisicalSdk.MachineIdentityCredential, err error) { - - var gcpIamAuthConfig GcpIamAuth - if err := ParseAuthConfig(tm.authConfigBytes, &gcpIamAuthConfig); err != nil { - return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to parse auth config due to error: %v", err) - } - - identityId, err := util.GetEnvVarOrFileContent(util.INFISICAL_MACHINE_IDENTITY_ID_NAME, gcpIamAuthConfig.IdentityID) - if err != nil { - return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to get identity id: %v", err) - } - - serviceAccountKeyPath := os.Getenv(util.INFISICAL_GCP_IAM_SERVICE_ACCOUNT_KEY_FILE_PATH_NAME) - if serviceAccountKeyPath == "" { - // we don't need to read this file, because the service account key path is directly read inside the sdk - serviceAccountKeyPath = gcpIamAuthConfig.ServiceAccountKey - if serviceAccountKeyPath == "" { - return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("gcp service account key path not found") - } - } - - return tm.infisicalClient.Auth().GcpIamAuthLogin(identityId, serviceAccountKeyPath) - -} - -func (tm *AgentManager) FetchAwsIamAuthAccessToken() (credential infisicalSdk.MachineIdentityCredential, err error) { - - var awsIamAuthConfig AwsIamAuth - if err := ParseAuthConfig(tm.authConfigBytes, &awsIamAuthConfig); err != nil { - return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to parse auth config due to error: %v", err) - } - - identityId, err := util.GetEnvVarOrFileContent(util.INFISICAL_MACHINE_IDENTITY_ID_NAME, awsIamAuthConfig.IdentityID) - - if err != nil { - return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to get identity id: %v", err) - } - - return tm.infisicalClient.Auth().AwsIamAuthLogin(identityId) - -} - -// Fetches a new access token using client credentials -func (tm *AgentManager) FetchNewAccessToken() error { - - authStrategies := map[util.AuthStrategyType]func() (credential infisicalSdk.MachineIdentityCredential, e error){ - util.AuthStrategy.UNIVERSAL_AUTH: tm.FetchUniversalAuthAccessToken, - util.AuthStrategy.KUBERNETES_AUTH: tm.FetchKubernetesAuthAccessToken, - util.AuthStrategy.AZURE_AUTH: tm.FetchAzureAuthAccessToken, - util.AuthStrategy.GCP_ID_TOKEN_AUTH: tm.FetchGcpIdTokenAuthAccessToken, - util.AuthStrategy.GCP_IAM_AUTH: tm.FetchGcpIamAuthAccessToken, - util.AuthStrategy.AWS_IAM_AUTH: tm.FetchAwsIamAuthAccessToken, - } - - if _, ok := authStrategies[tm.authStrategy]; !ok { - return fmt.Errorf("auth strategy %s not found", tm.authStrategy) - } - - credential, err := authStrategies[tm.authStrategy]() - - if err != nil { - return err - } - - accessTokenTTL := time.Duration(credential.ExpiresIn * int64(time.Second)) - accessTokenMaxTTL := time.Duration(credential.AccessTokenMaxTTL * int64(time.Second)) - - if accessTokenTTL <= time.Duration(5)*time.Second { - util.PrintErrorMessageAndExit("At this time, agent does not support refresh of tokens with 5 seconds or less ttl. Please increase access token ttl and try again") - } - - tm.accessTokenFetchedTime = time.Now() - tm.SetToken(credential.AccessToken, accessTokenTTL, accessTokenMaxTTL) - - return nil -} - -// Refreshes the existing access token -func (tm *AgentManager) RefreshAccessToken(accessToken string) error { - httpClient, err := util.GetRestyClientWithCustomHeaders() - if err != nil { - return err - } - - httpClient.SetRetryCount(10000). - SetRetryMaxWaitTime(20 * time.Second). - SetRetryWaitTime(5 * time.Second) - - response, err := api.CallMachineIdentityRefreshAccessToken(httpClient, api.UniversalAuthRefreshRequest{AccessToken: accessToken}) - if err != nil { - return err - } - - accessTokenTTL := time.Duration(response.AccessTokenTTL * int(time.Second)) - accessTokenMaxTTL := time.Duration(response.AccessTokenMaxTTL * int(time.Second)) - tm.accessTokenRefreshedTime = time.Now() - - tm.SetToken(response.AccessToken, accessTokenTTL, accessTokenMaxTTL) - - return nil -} - -func (tm *AgentManager) ManageTokenLifecycle() { - for { - accessTokenMaxTTLExpiresInTime := tm.accessTokenFetchedTime.Add(tm.accessTokenMaxTTL - (5 * time.Second)) - accessTokenRefreshedTime := tm.accessTokenRefreshedTime - - if accessTokenRefreshedTime.IsZero() { - accessTokenRefreshedTime = tm.accessTokenFetchedTime - } - - // Calculate next expiry time at 2/3 of the TTL - nextAccessTokenExpiresInTime := accessTokenRefreshedTime.Add(tm.accessTokenTTL * 2 / 3) - - if tm.accessTokenFetchedTime.IsZero() && tm.accessTokenRefreshedTime.IsZero() { - // try to fetch token from sink files first - // if token is found, refresh the token right away and continue from there - isSavedTokenValid := false - token := tm.FetchTokenFromFiles() - if token != "" { - log.Info().Msg("found existing token in file, attempting to refresh...") - err := tm.RefreshAccessToken(token) - isSavedTokenValid = err == nil - if isSavedTokenValid { - log.Info().Msg("token refreshed successfully from saved file") - tm.accessTokenFetchedTime = time.Now() - } else { - log.Error().Msg("unable to refresh token from saved file") - } - } - - if !isSavedTokenValid { - // case: init login to get access token - log.Info().Msg("attempting to authenticate...") - err := tm.FetchNewAccessToken() - if err != nil { - log.Error().Msgf("unable to authenticate because %v. Will retry in 30 seconds", err) - - // wait a bit before trying again - time.Sleep((30 * time.Second)) - continue - } - } - } else if time.Now().After(accessTokenMaxTTLExpiresInTime) { - // case: token has reached max ttl and we should re-authenticate entirely (cannot refresh) - log.Info().Msgf("token has reached max ttl, attempting to re authenticate...") - err := tm.FetchNewAccessToken() - if err != nil { - log.Error().Msgf("unable to authenticate because %v. Will retry in 30 seconds", err) - - // wait a bit before trying again - time.Sleep((30 * time.Second)) - continue - } - } else { - // case: token ttl has expired, but the token is still within max ttl, so we can refresh - log.Info().Msgf("attempting to refresh existing token...") - err := tm.RefreshAccessToken(tm.GetToken()) - if err != nil { - log.Error().Msgf("unable to refresh token because %v. Will retry in 30 seconds", err) - - // wait a bit before trying again - time.Sleep((30 * time.Second)) - continue - } - } - - if tm.exitAfterAuth { - time.Sleep(25 * time.Second) - os.Exit(0) - } - - if accessTokenRefreshedTime.IsZero() { - accessTokenRefreshedTime = tm.accessTokenFetchedTime - } else { - accessTokenRefreshedTime = tm.accessTokenRefreshedTime - } - - // Recalculate next expiry time at 2/3 of the TTL - nextAccessTokenExpiresInTime = accessTokenRefreshedTime.Add(tm.accessTokenTTL * 2 / 3) - accessTokenMaxTTLExpiresInTime = tm.accessTokenFetchedTime.Add(tm.accessTokenMaxTTL - (5 * time.Second)) - - if nextAccessTokenExpiresInTime.After(accessTokenMaxTTLExpiresInTime) { - // case: Refreshed so close that the next refresh would occur beyond max ttl - // Sleep until we're at 2/3 of the remaining time to max TTL - remainingTime := accessTokenMaxTTLExpiresInTime.Sub(time.Now()) - time.Sleep(remainingTime * 2 / 3) - } else { - // Sleep until we're at 2/3 of the TTL - time.Sleep(tm.accessTokenTTL * 2 / 3) - } - } -} - -func (tm *AgentManager) WriteTokenToFiles() { - token := tm.GetToken() - for _, sinkFile := range tm.filePaths { - if sinkFile.Type == "file" { - err := ioutil.WriteFile(sinkFile.Config.Path, []byte(token), 0644) - if err != nil { - log.Error().Msgf("unable to write file sink to path '%s' because %v", sinkFile.Config.Path, err) - } - - log.Info().Msgf("new access token saved to file at path '%s'", sinkFile.Config.Path) - - } else { - log.Error().Msg("unsupported sink type. Only 'file' type is supported") - } - } -} - -func (tm *AgentManager) FetchTokenFromFiles() string { - for _, sinkFile := range tm.filePaths { - if sinkFile.Type == "file" { - tokenBytes, err := ioutil.ReadFile(sinkFile.Config.Path) - if err != nil { - log.Debug().Msgf("unable to read token from file '%s' because %v", sinkFile.Config.Path, err) - continue - } - - token := string(tokenBytes) - if token != "" { - return token - } - } - } - return "" -} - -func (tm *AgentManager) WriteTemplateToFile(bytes *bytes.Buffer, template *Template) { - if err := WriteBytesToFile(bytes, template.DestinationPath); err != nil { - log.Error().Msgf("template engine: unable to write secrets to path because %s. Will try again on next cycle", err) - return - } - log.Info().Msgf("template engine: secret template at path %s has been rendered and saved to path %s", template.SourcePath, template.DestinationPath) -} - -func (tm *AgentManager) MonitorSecretChanges(secretTemplate Template, templateId int, sigChan chan os.Signal) { - - pollingInterval := time.Duration(5 * time.Minute) - - if secretTemplate.Config.PollingInterval != "" { - interval, err := util.ConvertPollingIntervalToTime(secretTemplate.Config.PollingInterval) - - if err != nil { - log.Error().Msgf("unable to convert polling interval to time because %v", err) - sigChan <- syscall.SIGINT - return - - } else { - pollingInterval = interval - } - } - - var existingEtag string - var currentEtag string - var firstRun = true - - execTimeout := secretTemplate.Config.Execute.Timeout - execCommand := secretTemplate.Config.Execute.Command - - for { - select { - case <-sigChan: - return - default: - { - tm.dynamicSecretLeases.Prune() - token := tm.GetToken() - if token != "" { - var processedTemplate *bytes.Buffer - var err error - - if secretTemplate.SourcePath != "" { - processedTemplate, err = ProcessTemplate(templateId, secretTemplate.SourcePath, nil, token, existingEtag, ¤tEtag, tm.dynamicSecretLeases) - } else if secretTemplate.TemplateContent != "" { - processedTemplate, err = ProcessLiteralTemplate(templateId, secretTemplate.TemplateContent, nil, token, existingEtag, ¤tEtag, tm.dynamicSecretLeases) - } else { - processedTemplate, err = ProcessBase64Template(templateId, secretTemplate.Base64TemplateContent, nil, token, existingEtag, ¤tEtag, tm.dynamicSecretLeases) - } - - if err != nil { - log.Error().Msgf("unable to process template because %v", err) - - // case: if exit-after-auth is true, it should exit the agent once an error on secret fetching occurs with the appropriate exit code (1) - // previous behavior would exit after 25 sec with status code 0, even if this step errors - if tm.exitAfterAuth { - os.Exit(1) - } - } else { - if (existingEtag != currentEtag) || firstRun { - - tm.WriteTemplateToFile(processedTemplate, &secretTemplate) - existingEtag = currentEtag - - if !firstRun && execCommand != "" { - log.Info().Msgf("executing command: %s", execCommand) - err := ExecuteCommandWithTimeout(execCommand, execTimeout) - - if err != nil { - log.Error().Msgf("unable to execute command because %v", err) - } - - } - if firstRun { - firstRun = false - } - } - } - - // now the idea is we pick the next sleep time in which the one shorter out of - // - polling time - // - first lease that's gonna get expired in the template - firstLeaseExpiry, isValid := tm.dynamicSecretLeases.GetFirstExpiringLeaseTime(templateId) - var waitTime = pollingInterval - if isValid && firstLeaseExpiry.Sub(time.Now()) < pollingInterval { - waitTime = firstLeaseExpiry.Sub(time.Now()) - } - time.Sleep(waitTime) - } else { - // It fails to get the access token. So we will re-try in 3 seconds. We do this because if we don't, the user will have to wait for the next polling interval to get the first secret render. - time.Sleep(3 * time.Second) - } - } - } - } -} - -// runCmd represents the run command -var agentCmd = &cobra.Command{ - Example: ` - infisical agent - `, - Use: "agent", - Short: "Used to launch a client daemon that streamlines authentication and secret retrieval processes in various environments", - DisableFlagsInUseLine: true, - Run: func(cmd *cobra.Command, args []string) { - - log.Info().Msg("starting Infisical agent...") - - configPath, err := cmd.Flags().GetString("config") - if err != nil { - util.HandleError(err, "Unable to parse flag config") - } - - var agentConfigInBytes []byte - - agentConfigInBase64 := os.Getenv("INFISICAL_AGENT_CONFIG_BASE64") - - if agentConfigInBase64 == "" { - data, err := ioutil.ReadFile(configPath) - if err != nil { - if !FileExists(configPath) { - log.Error().Msgf("Unable to locate %s. The provided agent config file path is either missing or incorrect", configPath) - return - } - } - agentConfigInBytes = data - } - - if agentConfigInBase64 != "" { - decodedAgentConfig, err := base64.StdEncoding.DecodeString(agentConfigInBase64) - if err != nil { - log.Error().Msgf("Unable to decode base64 config file because %v", err) - return - } - - agentConfigInBytes = decodedAgentConfig - } - - if !FileExists(configPath) && agentConfigInBase64 == "" { - log.Error().Msgf("No agent config file provided at %v. Please provide a agent config file", configPath) - return - } - - agentConfig, err := ParseAgentConfig(agentConfigInBytes) - if err != nil { - log.Error().Msgf("Unable to prase %s because %v. Please ensure that is follows the Infisical Agent config structure", configPath, err) - return - } - - authMethodValid, authStrategy := util.IsAuthMethodValid(agentConfig.Auth.Type, false) - - if !authMethodValid { - util.PrintErrorMessageAndExit(fmt.Sprintf("The auth method '%s' is not supported.", agentConfig.Auth.Type)) - } - - tokenRefreshNotifier := make(chan bool) - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) - - filePaths := agentConfig.Sinks - - configBytes, err := yaml.Marshal(agentConfig.Auth.Config) - if err != nil { - log.Error().Msgf("unable to marshal auth config because %v", err) - return - } - - tm := NewAgentManager(NewAgentMangerOptions{ - FileDeposits: filePaths, - Templates: agentConfig.Templates, - AuthConfigBytes: configBytes, - NewAccessTokenNotificationChan: tokenRefreshNotifier, - ExitAfterAuth: agentConfig.Infisical.ExitAfterAuth, - AuthStrategy: authStrategy, - }) - - tm.dynamicSecretLeases = NewDynamicSecretLeaseManager(sigChan) - - go tm.ManageTokenLifecycle() - - for i, template := range agentConfig.Templates { - log.Info().Msgf("template engine started for template %v...", i+1) - go tm.MonitorSecretChanges(template, i, sigChan) - } - - for { - select { - case <-tokenRefreshNotifier: - go tm.WriteTokenToFiles() - case <-sigChan: - log.Info().Msg("agent is gracefully shutting...") - // TODO: check if we are in the middle of writing files to disk - os.Exit(1) - } - } - - }, -} - -func init() { - agentCmd.SetHelpFunc(func(command *cobra.Command, strings []string) { - command.Flags().MarkHidden("domain") - command.Parent().HelpFunc()(command, strings) - }) - agentCmd.Flags().String("config", "agent-config.yaml", "The path to agent config yaml file") - rootCmd.AddCommand(agentCmd) -} diff --git a/cli/packages/cmd/bootstrap.go b/cli/packages/cmd/bootstrap.go deleted file mode 100644 index 7132b634d..000000000 --- a/cli/packages/cmd/bootstrap.go +++ /dev/null @@ -1,277 +0,0 @@ -/* -Copyright (c) 2023 Infisical Inc. -*/ -package cmd - -import ( - "bytes" - "context" - "encoding/base64" - "encoding/json" - "fmt" - "os" - "text/template" - - "github.com/Infisical/infisical-merge/packages/api" - "github.com/Infisical/infisical-merge/packages/util" - "github.com/rs/zerolog/log" - "github.com/spf13/cobra" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" -) - -// handleK8SecretOutput processes the k8-secret output type by creating a Kubernetes secret -func handleK8SecretOutput(bootstrapResponse api.BootstrapInstanceResponse, k8SecretTemplate, k8SecretName, k8SecretNamespace string) error { - // Create in-cluster config - config, err := rest.InClusterConfig() - if err != nil { - return fmt.Errorf("failed to create in-cluster config: %v", err) - } - - // Create Kubernetes client - clientset, err := kubernetes.NewForConfig(config) - if err != nil { - return fmt.Errorf("failed to create Kubernetes client: %v", err) - } - - // Parse and execute the template to render the data/stringData section - tmpl, err := template.New("k8-secret-template").Funcs(template.FuncMap{ - "encodeBase64": func(s string) string { - return base64.StdEncoding.EncodeToString([]byte(s)) - }, - }).Parse(k8SecretTemplate) - - if err != nil { - return fmt.Errorf("failed to parse output template: %v", err) - } - - var renderedDataSection bytes.Buffer - err = tmpl.Execute(&renderedDataSection, bootstrapResponse) - if err != nil { - return fmt.Errorf("failed to execute output template: %v", err) - } - - // Parse the rendered template as JSON to validate it's valid - var dataSection map[string]interface{} - if err := json.Unmarshal(renderedDataSection.Bytes(), &dataSection); err != nil { - return fmt.Errorf("template output is not valid JSON: %v", err) - } - - // Prepare the secret data and stringData maps - secretData := make(map[string][]byte) - secretStringData := make(map[string]string) - - // Process the dataSection to separate data and stringData - if data, exists := dataSection["data"]; exists { - if dataMap, ok := data.(map[string]interface{}); ok { - for key, value := range dataMap { - if strValue, ok := value.(string); ok { - secretData[key] = []byte(strValue) - } - } - } - } - - if stringData, exists := dataSection["stringData"]; exists { - if stringDataMap, ok := stringData.(map[string]interface{}); ok { - for key, value := range stringDataMap { - if strValue, ok := value.(string); ok { - secretStringData[key] = strValue - } - } - } - } - - // Create the Kubernetes secret object - k8sSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: k8SecretName, - Namespace: k8SecretNamespace, - }, - Type: corev1.SecretTypeOpaque, - Data: secretData, - StringData: secretStringData, - } - - ctx := context.Background() - secretsClient := clientset.CoreV1().Secrets(k8SecretNamespace) - - // Check if secret already exists - existingSecret, err := secretsClient.Get(ctx, k8SecretName, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - // Secret doesn't exist, create it - _, err = secretsClient.Create(ctx, k8sSecret, metav1.CreateOptions{}) - if err != nil { - return fmt.Errorf("failed to create Kubernetes secret: %v", err) - } - log.Info().Msgf("Successfully created Kubernetes secret '%s' in namespace '%s'", k8SecretName, k8SecretNamespace) - } else { - return fmt.Errorf("failed to check if Kubernetes secret exists: %v", err) - } - } else { - // Secret exists, update it - k8sSecret.ObjectMeta.ResourceVersion = existingSecret.ObjectMeta.ResourceVersion - _, err = secretsClient.Update(ctx, k8sSecret, metav1.UpdateOptions{}) - if err != nil { - return fmt.Errorf("failed to update Kubernetes secret: %v", err) - } - log.Info().Msgf("Successfully updated Kubernetes secret '%s' in namespace '%s'", k8SecretName, k8SecretNamespace) - } - - return nil -} - -var bootstrapCmd = &cobra.Command{ - Use: "bootstrap", - Short: "Used to bootstrap your Infisical instance", - DisableFlagsInUseLine: true, - Example: "infisical bootstrap", - Args: cobra.NoArgs, - Run: func(cmd *cobra.Command, args []string) { - email, _ := cmd.Flags().GetString("email") - if email == "" { - if envEmail, ok := os.LookupEnv(util.INFISICAL_BOOTSTRAP_EMAIL_NAME); ok { - email = envEmail - } - } - - if email == "" { - log.Error().Msg("email is required") - return - } - - password, _ := cmd.Flags().GetString("password") - if password == "" { - if envPassword, ok := os.LookupEnv(util.INFISICAL_BOOTSTRAP_PASSWORD_NAME); ok { - password = envPassword - } - } - - if password == "" { - log.Error().Msg("password is required") - return - } - - organization, _ := cmd.Flags().GetString("organization") - if organization == "" { - if envOrganization, ok := os.LookupEnv(util.INFISICAL_BOOTSTRAP_ORGANIZATION_NAME); ok { - organization = envOrganization - } - } - - if organization == "" { - log.Error().Msg("organization is required") - return - } - - domain, _ := cmd.Flags().GetString("domain") - if domain == "" { - if envDomain, ok := os.LookupEnv("INFISICAL_API_URL"); ok { - domain = envDomain - } - } - - if domain == "" { - log.Error().Msg("domain is required") - return - } - - outputType, err := cmd.Flags().GetString("output") - if err != nil { - log.Error().Msgf("Failed to get output type: %v", err) - return - } - - k8SecretTemplate, err := cmd.Flags().GetString("k8-secret-template") - if err != nil { - log.Error().Msgf("Failed to get k8-secret-template: %v", err) - } - - k8SecretName, err := cmd.Flags().GetString("k8-secret-name") - if err != nil { - log.Error().Msgf("Failed to get k8-secret-name: %v", err) - } - - k8SecretNamespace, err := cmd.Flags().GetString("k8-secret-namespace") - if err != nil { - log.Error().Msgf("Failed to get k8-secret-namespace: %v", err) - } - - if outputType == "k8-secret" { - if k8SecretTemplate == "" { - log.Error().Msg("k8-secret-template is required when using k8-secret output type") - return - } - - if k8SecretName == "" { - log.Error().Msg("k8-secret-name is required when using k8-secret output type") - return - } - - if k8SecretNamespace == "" { - log.Error().Msg("k8-secret-namespace is required when using k8-secret output type") - return - } - } - - httpClient, err := util.GetRestyClientWithCustomHeaders() - if err != nil { - log.Error().Msgf("Failed to get resty client with custom headers: %v", err) - return - } - - ignoreIfBootstrapped, err := cmd.Flags().GetBool("ignore-if-bootstrapped") - if err != nil { - log.Error().Msgf("Failed to get ignore-if-bootstrapped flag: %v", err) - return - } - - httpClient.SetHeader("Accept", "application/json") - - bootstrapResponse, err := api.CallBootstrapInstance(httpClient, api.BootstrapInstanceRequest{ - Domain: util.AppendAPIEndpoint(domain), - Email: email, - Password: password, - Organization: organization, - }) - - if err != nil { - if !ignoreIfBootstrapped { - log.Error().Msgf("Failed to bootstrap instance: %v", err) - } - return - } - - if outputType == "k8-secret" { - if err := handleK8SecretOutput(bootstrapResponse, k8SecretTemplate, k8SecretName, k8SecretNamespace); err != nil { - log.Error().Msgf("Failed to handle k8-secret output: %v", err) - return - } - } else { - responseJSON, err := json.MarshalIndent(bootstrapResponse, "", " ") - if err != nil { - log.Fatal().Msgf("Failed to convert response to JSON: %v", err) - return - } - - fmt.Println(string(responseJSON)) - } - }, -} - -func init() { - bootstrapCmd.Flags().String("domain", "", "The domain of your self-hosted Infisical instance") - bootstrapCmd.Flags().String("email", "", "The desired email address of the instance admin") - bootstrapCmd.Flags().String("password", "", "The desired password of the instance admin") - bootstrapCmd.Flags().String("organization", "", "The name of the organization to create for the instance") - bootstrapCmd.Flags().String("output", "", "The type of output to use for the bootstrap command (json or k8-secret)") - bootstrapCmd.Flags().Bool("ignore-if-bootstrapped", false, "Whether to continue on error if the instance has already been bootstrapped") - bootstrapCmd.Flags().String("k8-secret-template", "{\"data\":{\"token\":\"{{.Identity.Credentials.Token}}\"}}", "The template to use for rendering the Kubernetes secret (entire secret JSON)") - bootstrapCmd.Flags().String("k8-secret-namespace", "", "The namespace to create the Kubernetes secret in") - bootstrapCmd.Flags().String("k8-secret-name", "", "The name of the Kubernetes secret to create") - rootCmd.AddCommand(bootstrapCmd) -} diff --git a/cli/packages/cmd/cmd_test.go b/cli/packages/cmd/cmd_test.go deleted file mode 100644 index f77c6b07b..000000000 --- a/cli/packages/cmd/cmd_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package cmd - -import ( - "testing" - - "github.com/Infisical/infisical-merge/packages/models" -) - -func TestFilterReservedEnvVars(t *testing.T) { - - // some test env vars. - // HOME and PATH are reserved key words and should be filtered out - // XDG_SESSION_ID and LC_CTYPE are reserved key word prefixes and should be filtered out - // The filter function only checks the keys of the env map, so we dont need to set any values - env := map[string]models.SingleEnvironmentVariable{ - "test": {}, - "test2": {}, - "HOME": {}, - "PATH": {}, - "XDG_SESSION_ID": {}, - "LC_CTYPE": {}, - } - - // check to see if there are any reserved key words in secrets to inject - filterReservedEnvVars(env) - - if len(env) != 2 { - t.Errorf("Expected 2 secrets to be returned, got %d", len(env)) - } - if _, ok := env["test"]; !ok { - t.Errorf("Expected test to be returned") - } - if _, ok := env["test2"]; !ok { - t.Errorf("Expected test2 to be returned") - } - if _, ok := env["HOME"]; ok { - t.Errorf("Expected HOME to be filtered out") - } - if _, ok := env["PATH"]; ok { - t.Errorf("Expected PATH to be filtered out") - } - if _, ok := env["XDG_SESSION_ID"]; ok { - t.Errorf("Expected XDG_SESSION_ID to be filtered out") - } - if _, ok := env["LC_CTYPE"]; ok { - t.Errorf("Expected LC_CTYPE to be filtered out") - } - -} diff --git a/cli/packages/cmd/dynamic_secrets.go b/cli/packages/cmd/dynamic_secrets.go deleted file mode 100644 index 0443e7714..000000000 --- a/cli/packages/cmd/dynamic_secrets.go +++ /dev/null @@ -1,676 +0,0 @@ -/* -Copyright (c) 2023 Infisical Inc. -*/ -package cmd - -import ( - "context" - "fmt" - - "github.com/Infisical/infisical-merge/packages/api" - "github.com/Infisical/infisical-merge/packages/config" - "github.com/Infisical/infisical-merge/packages/visualize" - - // "github.com/Infisical/infisical-merge/packages/models" - "github.com/Infisical/infisical-merge/packages/util" - // "github.com/Infisical/infisical-merge/packages/visualize" - "github.com/posthog/posthog-go" - "github.com/spf13/cobra" - - infisicalSdk "github.com/infisical/go-sdk" - infisicalSdkModels "github.com/infisical/go-sdk/packages/models" -) - -var dynamicSecretCmd = &cobra.Command{ - Example: `infisical dynamic-secrets`, - Short: "Used to list dynamic secrets", - Use: "dynamic-secrets", - DisableFlagsInUseLine: true, - Args: cobra.NoArgs, - Run: getDynamicSecretList, -} - -func getDynamicSecretList(cmd *cobra.Command, args []string) { - environmentName, _ := cmd.Flags().GetString("env") - if !cmd.Flags().Changed("env") { - environmentFromWorkspace := util.GetEnvFromWorkspaceFile() - if environmentFromWorkspace != "" { - environmentName = environmentFromWorkspace - } - } - - token, err := util.GetInfisicalToken(cmd) - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - projectId, err := cmd.Flags().GetString("projectId") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - projectSlug, err := cmd.Flags().GetString("project-slug") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - secretsPath, err := cmd.Flags().GetString("path") - if err != nil { - util.HandleError(err, "Unable to parse path flag") - } - - var infisicalToken string - httpClient, err := util.GetRestyClientWithCustomHeaders() - if err != nil { - util.HandleError(err, "Unable to get resty client with custom headers") - } - - if projectId == "" && projectSlug == "" { - workspaceFile, err := util.GetWorkSpaceFromFile() - if err != nil { - util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project, pass in project slug with --project-slug flag, or pass in project id with --projectId flag") - } - projectId = workspaceFile.WorkspaceId - } - - if token != nil && (token.Type == util.SERVICE_TOKEN_IDENTIFIER || token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER) { - infisicalToken = token.Token - } else { - util.RequireLogin() - - loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true) - if err != nil { - util.HandleError(err, "Unable to authenticate") - } - - if loggedInUserDetails.LoginExpired { - loggedInUserDetails = util.EstablishUserLoginSession() - } - - infisicalToken = loggedInUserDetails.UserCredentials.JTWToken - } - - httpClient.SetAuthToken(infisicalToken) - - customHeaders, err := util.GetInfisicalCustomHeadersMap() - if err != nil { - util.HandleError(err, "Unable to get custom headers") - } - - infisicalClient := infisicalSdk.NewInfisicalClient(context.Background(), infisicalSdk.Config{ - SiteUrl: config.INFISICAL_URL, - UserAgent: api.USER_AGENT, - AutoTokenRefresh: false, - CustomHeaders: customHeaders, - }) - infisicalClient.Auth().SetAccessToken(infisicalToken) - - if projectSlug == "" { - projectDetails, err := api.CallGetProjectById(httpClient, projectId) - if err != nil { - util.HandleError(err, "To fetch project details") - } - projectSlug = projectDetails.Slug - } - - dynamicSecretRootCredentials, err := infisicalClient.DynamicSecrets().List(infisicalSdk.ListDynamicSecretsRootCredentialsOptions{ - ProjectSlug: projectSlug, - SecretPath: secretsPath, - EnvironmentSlug: environmentName, - }) - - if err != nil { - util.HandleError(err, "To fetch dynamic secret root credentials details") - } - - visualize.PrintAllDynamicRootCredentials(dynamicSecretRootCredentials) - Telemetry.CaptureEvent("cli-command:dynamic-secrets", posthog.NewProperties().Set("count", len(dynamicSecretRootCredentials)).Set("version", util.CLI_VERSION)) -} - -var dynamicSecretLeaseCmd = &cobra.Command{ - Example: `lease`, - Short: "Manage leases for dynamic secrets", - Use: "lease", - DisableFlagsInUseLine: true, -} - -var dynamicSecretLeaseCreateCmd = &cobra.Command{ - Example: `lease create "`, - Short: "Used to lease dynamic secret by name", - Use: "create [dynamic-secret]", - DisableFlagsInUseLine: true, - Args: cobra.ExactArgs(1), - Run: createDynamicSecretLeaseByName, -} - -func createDynamicSecretLeaseByName(cmd *cobra.Command, args []string) { - dynamicSecretRootCredentialName := args[0] - - environmentName, _ := cmd.Flags().GetString("env") - if !cmd.Flags().Changed("env") { - environmentFromWorkspace := util.GetEnvFromWorkspaceFile() - if environmentFromWorkspace != "" { - environmentName = environmentFromWorkspace - } - } - - token, err := util.GetInfisicalToken(cmd) - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - projectId, err := cmd.Flags().GetString("projectId") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - projectSlug, err := cmd.Flags().GetString("project-slug") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - ttl, err := cmd.Flags().GetString("ttl") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - secretsPath, err := cmd.Flags().GetString("path") - if err != nil { - util.HandleError(err, "Unable to parse path flag") - } - - plainOutput, err := cmd.Flags().GetBool("plain") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - var infisicalToken string - httpClient, err := util.GetRestyClientWithCustomHeaders() - if err != nil { - util.HandleError(err, "Unable to get resty client with custom headers") - } - - if projectId == "" && projectSlug == "" { - workspaceFile, err := util.GetWorkSpaceFromFile() - if err != nil { - util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project, pass in project id with --projectId flag, or pass in project slug with --project-slug flag") - } - projectId = workspaceFile.WorkspaceId - } - - if token != nil && (token.Type == util.SERVICE_TOKEN_IDENTIFIER || token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER) { - infisicalToken = token.Token - } else { - util.RequireLogin() - - loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true) - if err != nil { - util.HandleError(err, "Unable to authenticate") - } - - if loggedInUserDetails.LoginExpired { - loggedInUserDetails = util.EstablishUserLoginSession() - } - infisicalToken = loggedInUserDetails.UserCredentials.JTWToken - } - - httpClient.SetAuthToken(infisicalToken) - - customHeaders, err := util.GetInfisicalCustomHeadersMap() - if err != nil { - util.HandleError(err, "Unable to get custom headers") - } - - infisicalClient := infisicalSdk.NewInfisicalClient(context.Background(), infisicalSdk.Config{ - SiteUrl: config.INFISICAL_URL, - UserAgent: api.USER_AGENT, - AutoTokenRefresh: false, - CustomHeaders: customHeaders, - }) - infisicalClient.Auth().SetAccessToken(infisicalToken) - - if projectSlug == "" { - projectDetails, err := api.CallGetProjectById(httpClient, projectId) - if err != nil { - util.HandleError(err, "To fetch project details") - } - projectSlug = projectDetails.Slug - } - - dynamicSecretRootCredential, err := infisicalClient.DynamicSecrets().GetByName(infisicalSdk.GetDynamicSecretRootCredentialByNameOptions{ - DynamicSecretName: dynamicSecretRootCredentialName, - ProjectSlug: projectSlug, - SecretPath: secretsPath, - EnvironmentSlug: environmentName, - }) - - if err != nil { - util.HandleError(err, "To fetch dynamic secret root credentials details") - } - - // for Kubernetes dynamic secrets only - kubernetesNamespace, err := cmd.Flags().GetString("kubernetes-namespace") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - config := map[string]any{} - if kubernetesNamespace != "" { - config["namespace"] = kubernetesNamespace - } - - leaseCredentials, _, leaseDetails, err := infisicalClient.DynamicSecrets().Leases().Create(infisicalSdk.CreateDynamicSecretLeaseOptions{ - DynamicSecretName: dynamicSecretRootCredential.Name, - ProjectSlug: projectSlug, - TTL: ttl, - SecretPath: secretsPath, - EnvironmentSlug: environmentName, - Config: config, - }) - - if err != nil { - util.HandleError(err, "To lease dynamic secret") - } - - if plainOutput { - for key, value := range leaseCredentials { - if cred, ok := value.(string); ok { - fmt.Printf("%s=%s\n", key, cred) - } - } - } else { - fmt.Println("Dynamic Secret Leasing") - fmt.Printf("Name: %s\n", dynamicSecretRootCredential.Name) - fmt.Printf("Provider: %s\n", dynamicSecretRootCredential.Type) - fmt.Printf("Lease ID: %s\n", leaseDetails.Id) - fmt.Printf("Expire At: %s\n", leaseDetails.ExpireAt.Local().Format("02-Jan-2006 03:04:05 PM")) - visualize.PrintAllDyamicSecretLeaseCredentials(leaseCredentials) - } - - Telemetry.CaptureEvent("cli-command:dynamic-secrets lease", posthog.NewProperties().Set("type", dynamicSecretRootCredential.Type).Set("version", util.CLI_VERSION)) -} - -var dynamicSecretLeaseRenewCmd = &cobra.Command{ - Example: `lease renew "`, - Short: "Used to renew dynamic secret lease by name", - Use: "renew [lease-id]", - DisableFlagsInUseLine: true, - Args: cobra.ExactArgs(1), - Run: renewDynamicSecretLeaseByName, -} - -func renewDynamicSecretLeaseByName(cmd *cobra.Command, args []string) { - dynamicSecretLeaseId := args[0] - - environmentName, _ := cmd.Flags().GetString("env") - if !cmd.Flags().Changed("env") { - environmentFromWorkspace := util.GetEnvFromWorkspaceFile() - if environmentFromWorkspace != "" { - environmentName = environmentFromWorkspace - } - } - - token, err := util.GetInfisicalToken(cmd) - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - projectId, err := cmd.Flags().GetString("projectId") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - projectSlug, err := cmd.Flags().GetString("project-slug") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - ttl, err := cmd.Flags().GetString("ttl") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - secretsPath, err := cmd.Flags().GetString("path") - if err != nil { - util.HandleError(err, "Unable to parse path flag") - } - - var infisicalToken string - httpClient, err := util.GetRestyClientWithCustomHeaders() - if err != nil { - util.HandleError(err, "Unable to get resty client with custom headers") - } - - if projectId == "" && projectSlug == "" { - workspaceFile, err := util.GetWorkSpaceFromFile() - if err != nil { - util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project, pass in project slug with --project-slug flag, or pass in project id with --projectId flag") - } - projectId = workspaceFile.WorkspaceId - } - - if token != nil && (token.Type == util.SERVICE_TOKEN_IDENTIFIER || token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER) { - infisicalToken = token.Token - } else { - util.RequireLogin() - - loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true) - if err != nil { - util.HandleError(err, "Unable to authenticate") - } - - if loggedInUserDetails.LoginExpired { - loggedInUserDetails = util.EstablishUserLoginSession() - } - - infisicalToken = loggedInUserDetails.UserCredentials.JTWToken - } - - httpClient.SetAuthToken(infisicalToken) - - customHeaders, err := util.GetInfisicalCustomHeadersMap() - if err != nil { - util.HandleError(err, "Unable to get custom headers") - } - - infisicalClient := infisicalSdk.NewInfisicalClient(context.Background(), infisicalSdk.Config{ - SiteUrl: config.INFISICAL_URL, - UserAgent: api.USER_AGENT, - AutoTokenRefresh: false, - CustomHeaders: customHeaders, - }) - infisicalClient.Auth().SetAccessToken(infisicalToken) - - if projectSlug == "" { - projectDetails, err := api.CallGetProjectById(httpClient, projectId) - if err != nil { - util.HandleError(err, "To fetch project details") - } - projectSlug = projectDetails.Slug - } - - if err != nil { - util.HandleError(err, "To fetch dynamic secret root credentials details") - } - - leaseDetails, err := infisicalClient.DynamicSecrets().Leases().RenewById(infisicalSdk.RenewDynamicSecretLeaseOptions{ - ProjectSlug: projectSlug, - TTL: ttl, - SecretPath: secretsPath, - EnvironmentSlug: environmentName, - LeaseId: dynamicSecretLeaseId, - }) - if err != nil { - util.HandleError(err, "To renew dynamic secret lease") - } - - fmt.Println("Successfully renewed dynamic secret lease") - visualize.PrintAllDynamicSecretLeases([]infisicalSdkModels.DynamicSecretLease{leaseDetails}) - - Telemetry.CaptureEvent("cli-command:dynamic-secrets lease renew", posthog.NewProperties().Set("version", util.CLI_VERSION)) -} - -var dynamicSecretLeaseRevokeCmd = &cobra.Command{ - Example: `lease delete "`, - Short: "Used to delete dynamic secret lease by name", - Use: "delete [lease-id]", - DisableFlagsInUseLine: true, - Args: cobra.ExactArgs(1), - Run: revokeDynamicSecretLeaseByName, -} - -func revokeDynamicSecretLeaseByName(cmd *cobra.Command, args []string) { - dynamicSecretLeaseId := args[0] - - environmentName, _ := cmd.Flags().GetString("env") - if !cmd.Flags().Changed("env") { - environmentFromWorkspace := util.GetEnvFromWorkspaceFile() - if environmentFromWorkspace != "" { - environmentName = environmentFromWorkspace - } - } - - token, err := util.GetInfisicalToken(cmd) - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - projectId, err := cmd.Flags().GetString("projectId") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - projectSlug, err := cmd.Flags().GetString("project-slug") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - secretsPath, err := cmd.Flags().GetString("path") - if err != nil { - util.HandleError(err, "Unable to parse path flag") - } - - var infisicalToken string - httpClient, err := util.GetRestyClientWithCustomHeaders() - if err != nil { - util.HandleError(err, "Unable to get resty client with custom headers") - } - - if projectId == "" && projectSlug == "" { - workspaceFile, err := util.GetWorkSpaceFromFile() - if err != nil { - util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project, pass in project slug with --project-slug flag, or pass in project id with --projectId flag") - } - projectId = workspaceFile.WorkspaceId - } - - if token != nil && (token.Type == util.SERVICE_TOKEN_IDENTIFIER || token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER) { - infisicalToken = token.Token - } else { - util.RequireLogin() - - loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true) - if err != nil { - util.HandleError(err, "Unable to authenticate") - } - - if loggedInUserDetails.LoginExpired { - loggedInUserDetails = util.EstablishUserLoginSession() - } - - infisicalToken = loggedInUserDetails.UserCredentials.JTWToken - } - - httpClient.SetAuthToken(infisicalToken) - - customHeaders, err := util.GetInfisicalCustomHeadersMap() - if err != nil { - util.HandleError(err, "Unable to get custom headers") - } - - infisicalClient := infisicalSdk.NewInfisicalClient(context.Background(), infisicalSdk.Config{ - SiteUrl: config.INFISICAL_URL, - UserAgent: api.USER_AGENT, - AutoTokenRefresh: false, - CustomHeaders: customHeaders, - }) - infisicalClient.Auth().SetAccessToken(infisicalToken) - - if projectSlug == "" { - projectDetails, err := api.CallGetProjectById(httpClient, projectId) - if err != nil { - util.HandleError(err, "To fetch project details") - } - projectSlug = projectDetails.Slug - } - - if err != nil { - util.HandleError(err, "To fetch dynamic secret root credentials details") - } - - leaseDetails, err := infisicalClient.DynamicSecrets().Leases().DeleteById(infisicalSdk.DeleteDynamicSecretLeaseOptions{ - ProjectSlug: projectSlug, - SecretPath: secretsPath, - EnvironmentSlug: environmentName, - LeaseId: dynamicSecretLeaseId, - }) - - if err != nil { - util.HandleError(err, "To revoke dynamic secret lease") - } - - fmt.Println("Successfully revoked dynamic secret lease") - visualize.PrintAllDynamicSecretLeases([]infisicalSdkModels.DynamicSecretLease{leaseDetails}) - - Telemetry.CaptureEvent("cli-command:dynamic-secrets lease revoke", posthog.NewProperties().Set("version", util.CLI_VERSION)) -} - -var dynamicSecretLeaseListCmd = &cobra.Command{ - Example: `lease list "`, - Short: "Used to list leases of a dynamic secret by name", - Use: "list [dynamic-secret]", - DisableFlagsInUseLine: true, - Args: cobra.ExactArgs(1), - Run: listDynamicSecretLeaseByName, -} - -func listDynamicSecretLeaseByName(cmd *cobra.Command, args []string) { - dynamicSecretRootCredentialName := args[0] - - environmentName, _ := cmd.Flags().GetString("env") - if !cmd.Flags().Changed("env") { - environmentFromWorkspace := util.GetEnvFromWorkspaceFile() - if environmentFromWorkspace != "" { - environmentName = environmentFromWorkspace - } - } - - token, err := util.GetInfisicalToken(cmd) - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - projectId, err := cmd.Flags().GetString("projectId") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - projectSlug, err := cmd.Flags().GetString("project-slug") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - secretsPath, err := cmd.Flags().GetString("path") - if err != nil { - util.HandleError(err, "Unable to parse path flag") - } - - var infisicalToken string - httpClient, err := util.GetRestyClientWithCustomHeaders() - if err != nil { - util.HandleError(err, "Unable to get resty client with custom headers") - } - - if projectId == "" && projectSlug == "" { - workspaceFile, err := util.GetWorkSpaceFromFile() - if err != nil { - util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project, pass in project slug with --project-slug flag, or pass in project id with --projectId flag") - } - projectId = workspaceFile.WorkspaceId - } - - if token != nil && (token.Type == util.SERVICE_TOKEN_IDENTIFIER || token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER) { - infisicalToken = token.Token - } else { - util.RequireLogin() - - loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true) - if err != nil { - util.HandleError(err, "Unable to authenticate") - } - - if loggedInUserDetails.LoginExpired { - loggedInUserDetails = util.EstablishUserLoginSession() - } - infisicalToken = loggedInUserDetails.UserCredentials.JTWToken - } - - httpClient.SetAuthToken(infisicalToken) - - customHeaders, err := util.GetInfisicalCustomHeadersMap() - if err != nil { - util.HandleError(err, "Unable to get custom headers") - } - - infisicalClient := infisicalSdk.NewInfisicalClient(context.Background(), infisicalSdk.Config{ - SiteUrl: config.INFISICAL_URL, - UserAgent: api.USER_AGENT, - AutoTokenRefresh: false, - CustomHeaders: customHeaders, - }) - infisicalClient.Auth().SetAccessToken(infisicalToken) - - if projectSlug == "" { - projectDetails, err := api.CallGetProjectById(httpClient, projectId) - if err != nil { - util.HandleError(err, "To fetch project details") - } - projectSlug = projectDetails.Slug - } - - dynamicSecretLeases, err := infisicalClient.DynamicSecrets().Leases().List(infisicalSdk.ListDynamicSecretLeasesOptions{ - DynamicSecretName: dynamicSecretRootCredentialName, - ProjectSlug: projectSlug, - SecretPath: secretsPath, - EnvironmentSlug: environmentName, - }) - - if err != nil { - util.HandleError(err, "To fetch dynamic secret leases list") - } - - visualize.PrintAllDynamicSecretLeases(dynamicSecretLeases) - Telemetry.CaptureEvent("cli-command:dynamic-secrets lease list", posthog.NewProperties().Set("lease-count", len(dynamicSecretLeases)).Set("version", util.CLI_VERSION)) -} - -func init() { - dynamicSecretLeaseCreateCmd.Flags().StringP("path", "p", "/", "The path from where dynamic secret should be leased from") - dynamicSecretLeaseCreateCmd.Flags().String("token", "", "Create dynamic secret leases using machine identity access token") - dynamicSecretLeaseCreateCmd.Flags().String("projectId", "", "Manually set the projectId to fetch leased from when using machine identity based auth") - dynamicSecretLeaseCreateCmd.Flags().String("project-slug", "", "Manually set the project-slug to create lease in") - dynamicSecretLeaseCreateCmd.Flags().String("ttl", "", "The lease lifetime TTL. If not provided the default TTL of dynamic secret will be used.") - dynamicSecretLeaseCreateCmd.Flags().Bool("plain", false, "Print leased credentials without formatting, one per line") - - // Kubernetes specific flags - dynamicSecretLeaseCreateCmd.Flags().String("kubernetes-namespace", "", "The namespace to create the lease in. Only used for Kubernetes dynamic secrets.") - - dynamicSecretLeaseCmd.AddCommand(dynamicSecretLeaseCreateCmd) - - dynamicSecretLeaseListCmd.Flags().StringP("path", "p", "/", "The path from where dynamic secret should be leased from") - dynamicSecretLeaseListCmd.Flags().String("token", "", "Fetch dynamic secret leases machine identity access token") - dynamicSecretLeaseListCmd.Flags().String("projectId", "", "Manually set the projectId to fetch leased from when using machine identity based auth") - dynamicSecretLeaseListCmd.Flags().String("project-slug", "", "Manually set the project-slug to list leases from") - dynamicSecretLeaseCmd.AddCommand(dynamicSecretLeaseListCmd) - - dynamicSecretLeaseRenewCmd.Flags().StringP("path", "p", "/", "The path from where dynamic secret should be leased from") - dynamicSecretLeaseRenewCmd.Flags().String("token", "", "Renew dynamic secrets machine identity access token") - dynamicSecretLeaseRenewCmd.Flags().String("projectId", "", "Manually set the projectId to fetch leased from when using machine identity based auth") - dynamicSecretLeaseRenewCmd.Flags().String("project-slug", "", "Manually set the project-slug to renew lease in") - dynamicSecretLeaseRenewCmd.Flags().String("ttl", "", "The lease lifetime TTL. If not provided the default TTL of dynamic secret will be used.") - dynamicSecretLeaseCmd.AddCommand(dynamicSecretLeaseRenewCmd) - - dynamicSecretLeaseRevokeCmd.Flags().StringP("path", "p", "/", "The path from where dynamic secret should be leased from") - dynamicSecretLeaseRevokeCmd.Flags().String("token", "", "Delete dynamic secrets using machine identity access token") - dynamicSecretLeaseRevokeCmd.Flags().String("projectId", "", "Manually set the projectId to fetch leased from when using machine identity based auth") - dynamicSecretLeaseRevokeCmd.Flags().String("project-slug", "", "Manually set the project-slug to revoke lease from") - dynamicSecretLeaseCmd.AddCommand(dynamicSecretLeaseRevokeCmd) - - dynamicSecretCmd.AddCommand(dynamicSecretLeaseCmd) - - dynamicSecretCmd.Flags().String("token", "", "Fetch secrets using service token or machine identity access token") - dynamicSecretCmd.Flags().String("projectId", "", "Manually set the projectId to fetch dynamic-secret when using machine identity based auth") - dynamicSecretCmd.Flags().String("project-slug", "", "Manually set the project-slug to fetch dynamic-secret from") - dynamicSecretCmd.PersistentFlags().String("env", "dev", "Used to select the environment name on which actions should be taken on") - dynamicSecretCmd.Flags().String("path", "/", "get dynamic secret within a folder path") - rootCmd.AddCommand(dynamicSecretCmd) -} diff --git a/cli/packages/cmd/export.go b/cli/packages/cmd/export.go deleted file mode 100644 index b872b0e61..000000000 --- a/cli/packages/cmd/export.go +++ /dev/null @@ -1,240 +0,0 @@ -/* -Copyright (c) 2023 Infisical Inc. -*/ -package cmd - -import ( - "encoding/csv" - "encoding/json" - "fmt" - "os" - "strings" - - "github.com/Infisical/infisical-merge/packages/models" - "github.com/Infisical/infisical-merge/packages/util" - "github.com/rs/zerolog/log" - "github.com/spf13/cobra" - "gopkg.in/yaml.v2" -) - -const ( - FormatDotenv string = "dotenv" - FormatJson string = "json" - FormatCSV string = "csv" - FormatYaml string = "yaml" - FormatDotEnvExport string = "dotenv-export" -) - -// exportCmd represents the export command -var exportCmd = &cobra.Command{ - Use: "export", - Short: "Used to export environment variables to a file", - DisableFlagsInUseLine: true, - Example: "infisical export --env=prod --format=json > secrets.json", - Args: cobra.NoArgs, - Run: func(cmd *cobra.Command, args []string) { - environmentName, _ := cmd.Flags().GetString("env") - if !cmd.Flags().Changed("env") { - environmentFromWorkspace := util.GetEnvFromWorkspaceFile() - if environmentFromWorkspace != "" { - environmentName = environmentFromWorkspace - } - } - - shouldExpandSecrets, err := cmd.Flags().GetBool("expand") - if err != nil { - util.HandleError(err) - } - - includeImports, err := cmd.Flags().GetBool("include-imports") - if err != nil { - util.HandleError(err) - } - - projectId, err := cmd.Flags().GetString("projectId") - if err != nil { - util.HandleError(err) - } - - token, err := util.GetInfisicalToken(cmd) - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - format, err := cmd.Flags().GetString("format") - if err != nil { - util.HandleError(err) - } - - templatePath, err := cmd.Flags().GetString("template") - if err != nil { - util.HandleError(err) - } - - secretOverriding, err := cmd.Flags().GetBool("secret-overriding") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - tagSlugs, err := cmd.Flags().GetString("tags") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - secretsPath, err := cmd.Flags().GetString("path") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - request := models.GetAllSecretsParameters{ - Environment: environmentName, - TagSlugs: tagSlugs, - WorkspaceId: projectId, - SecretsPath: secretsPath, - IncludeImport: includeImports, - ExpandSecretReferences: shouldExpandSecrets, - } - - if token != nil && token.Type == util.SERVICE_TOKEN_IDENTIFIER { - request.InfisicalToken = token.Token - } else if token != nil && token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER { - request.UniversalAuthAccessToken = token.Token - } - - if templatePath != "" { - sigChan := make(chan os.Signal, 1) - dynamicSecretLeases := NewDynamicSecretLeaseManager(sigChan) - newEtag := "" - - accessToken := "" - if token != nil { - accessToken = token.Token - } else { - log.Debug().Msg("GetAllEnvironmentVariables: Trying to fetch secrets using logged in details") - loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true) - if err != nil { - util.HandleError(err) - } - accessToken = loggedInUserDetails.UserCredentials.JTWToken - } - - processedTemplate, err := ProcessTemplate(1, templatePath, nil, accessToken, "", &newEtag, dynamicSecretLeases) - if err != nil { - util.HandleError(err) - } - fmt.Print(processedTemplate.String()) - return - } - - secrets, err := util.GetAllEnvironmentVariables(request, "") - if err != nil { - util.HandleError(err, "Unable to fetch secrets") - } - - if secretOverriding { - secrets = util.OverrideSecrets(secrets, util.SECRET_TYPE_PERSONAL) - } else { - secrets = util.OverrideSecrets(secrets, util.SECRET_TYPE_SHARED) - } - - var output string - secrets = util.FilterSecretsByTag(secrets, tagSlugs) - secrets = util.SortSecretsByKeys(secrets) - - output, err = formatEnvs(secrets, format) - if err != nil { - util.HandleError(err) - } - - fmt.Print(output) - - // Telemetry.CaptureEvent("cli-command:export", posthog.NewProperties().Set("secretsCount", len(secrets)).Set("version", util.CLI_VERSION)) - }, -} - -func init() { - rootCmd.AddCommand(exportCmd) - exportCmd.Flags().StringP("env", "e", "dev", "Set the environment (dev, prod, etc.) from which your secrets should be pulled from") - exportCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets") - exportCmd.Flags().StringP("format", "f", "dotenv", "Set the format of the output file (dotenv, json, csv)") - exportCmd.Flags().Bool("secret-overriding", true, "Prioritizes personal secrets, if any, with the same name over shared secrets") - exportCmd.Flags().Bool("include-imports", true, "Imported linked secrets") - exportCmd.Flags().String("token", "", "Fetch secrets using service token or machine identity access token") - exportCmd.Flags().StringP("tags", "t", "", "filter secrets by tag slugs") - exportCmd.Flags().String("projectId", "", "manually set the projectId to export secrets from") - exportCmd.Flags().String("path", "/", "get secrets within a folder path") - exportCmd.Flags().String("template", "", "The path to the template file used to render secrets") -} - -// Format according to the format flag -func formatEnvs(envs []models.SingleEnvironmentVariable, format string) (string, error) { - switch strings.ToLower(format) { - case FormatDotenv: - return formatAsDotEnv(envs), nil - case FormatDotEnvExport: - return formatAsDotEnvExport(envs), nil - case FormatJson: - return formatAsJson(envs), nil - case FormatCSV: - return formatAsCSV(envs), nil - case FormatYaml: - return formatAsYaml(envs) - default: - return "", fmt.Errorf("invalid format type: %s. Available format types are [%s]", format, []string{FormatDotenv, FormatJson, FormatCSV, FormatYaml, FormatDotEnvExport}) - } -} - -// Format environment variables as a CSV file -func formatAsCSV(envs []models.SingleEnvironmentVariable) string { - csvString := &strings.Builder{} - writer := csv.NewWriter(csvString) - writer.Write([]string{"Key", "Value"}) - for _, env := range envs { - writer.Write([]string{env.Key, env.Value}) - } - writer.Flush() - return csvString.String() -} - -// Format environment variables as a dotenv file -func formatAsDotEnv(envs []models.SingleEnvironmentVariable) string { - var dotenv string - for _, env := range envs { - dotenv += fmt.Sprintf("%s='%s'\n", env.Key, env.Value) - } - return dotenv -} - -// Format environment variables as a dotenv file with export at the beginning -func formatAsDotEnvExport(envs []models.SingleEnvironmentVariable) string { - var dotenv string - for _, env := range envs { - dotenv += fmt.Sprintf("export %s='%s'\n", env.Key, env.Value) - } - return dotenv -} - -func formatAsYaml(envs []models.SingleEnvironmentVariable) (string, error) { - m := make(map[string]string) - for _, env := range envs { - m[env.Key] = env.Value - } - - yamlBytes, err := yaml.Marshal(m) - if err != nil { - return "", fmt.Errorf("failed to format environment variables as YAML: %w", err) - } - - return string(yamlBytes), nil -} - -// Format environment variables as a JSON file -func formatAsJson(envs []models.SingleEnvironmentVariable) string { - // Dump as a json array - json, err := json.Marshal(envs) - if err != nil { - log.Err(err).Msgf("Unable to marshal environment variables to JSON") - return "" - } - return string(json) -} diff --git a/cli/packages/cmd/export_test.go b/cli/packages/cmd/export_test.go deleted file mode 100644 index 1be0a7ed2..000000000 --- a/cli/packages/cmd/export_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package cmd - -import ( - "testing" - - "github.com/Infisical/infisical-merge/packages/models" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v2" -) - -func TestFormatAsYaml(t *testing.T) { - tests := []struct { - name string - input []models.SingleEnvironmentVariable - expected string - }{ - { - name: "Empty input", - input: []models.SingleEnvironmentVariable{}, - expected: "{}\n", - }, - { - name: "Single environment variable", - input: []models.SingleEnvironmentVariable{ - {Key: "KEY1", Value: "VALUE1"}, - }, - expected: "KEY1: VALUE1\n", - }, - { - name: "Multiple environment variables", - input: []models.SingleEnvironmentVariable{ - {Key: "KEY1", Value: "VALUE1"}, - {Key: "KEY2", Value: "VALUE2"}, - {Key: "KEY3", Value: "VALUE3"}, - }, - expected: "KEY1: VALUE1\nKEY2: VALUE2\nKEY3: VALUE3\n", - }, - { - name: "Overwriting duplicate keys", - input: []models.SingleEnvironmentVariable{ - {Key: "KEY1", Value: "VALUE1"}, - {Key: "KEY1", Value: "VALUE2"}, - }, - expected: "KEY1: VALUE2\n", - }, - { - name: "Special characters in values", - input: []models.SingleEnvironmentVariable{ - {Key: "KEY1", Value: "Value with spaces"}, - {Key: "KEY2", Value: "Value:with:colons"}, - {Key: "KEY3", Value: "Value\nwith\nnewlines"}, - }, - expected: "KEY1: Value with spaces\nKEY2: Value:with:colons\nKEY3: |-\n Value\n with\n newlines\n", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result, err := formatAsYaml(tt.input) - assert.NoError(t, err) - - // Compare the result with the expected output - assert.Equal(t, tt.expected, result) - - // Additionally, parse the result back into a map to ensure it's valid YAML - var resultMap map[string]string - err = yaml.Unmarshal([]byte(result), &resultMap) - assert.NoError(t, err) - - // Create an expected map from the input - expectedMap := make(map[string]string) - for _, env := range tt.input { - expectedMap[env.Key] = env.Value - } - - assert.Equal(t, expectedMap, resultMap) - }) - } -} diff --git a/cli/packages/cmd/folder.go b/cli/packages/cmd/folder.go deleted file mode 100644 index b59652191..000000000 --- a/cli/packages/cmd/folder.go +++ /dev/null @@ -1,209 +0,0 @@ -package cmd - -import ( - "errors" - "fmt" - - "github.com/Infisical/infisical-merge/packages/models" - "github.com/Infisical/infisical-merge/packages/util" - "github.com/Infisical/infisical-merge/packages/visualize" - "github.com/posthog/posthog-go" - "github.com/spf13/cobra" -) - -var folderCmd = &cobra.Command{ - Use: "folders", - Short: "Create, delete, and list folders", - DisableFlagsInUseLine: true, - Run: func(cmd *cobra.Command, args []string) { - cmd.Help() - }, -} - -var getCmd = &cobra.Command{ - Use: "get", - Short: "Get folders in a directory", - Run: func(cmd *cobra.Command, args []string) { - - environmentName, _ := cmd.Flags().GetString("env") - if !cmd.Flags().Changed("env") { - environmentFromWorkspace := util.GetEnvFromWorkspaceFile() - if environmentFromWorkspace != "" { - environmentName = environmentFromWorkspace - } - } - - projectId, err := cmd.Flags().GetString("projectId") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - token, err := util.GetInfisicalToken(cmd) - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - foldersPath, err := cmd.Flags().GetString("path") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - request := models.GetAllFoldersParameters{ - Environment: environmentName, - WorkspaceId: projectId, - FoldersPath: foldersPath, - } - - if token != nil && token.Type == util.SERVICE_TOKEN_IDENTIFIER { - request.InfisicalToken = token.Token - } else if token != nil && token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER { - request.UniversalAuthAccessToken = token.Token - } - - folders, err := util.GetAllFolders(request) - if err != nil { - util.HandleError(err, "Unable to get folders") - } - - visualize.PrintAllFoldersDetails(folders, foldersPath) - Telemetry.CaptureEvent("cli-command:folders get", posthog.NewProperties().Set("folderCount", len(folders)).Set("version", util.CLI_VERSION)) - }, -} - -var createCmd = &cobra.Command{ - Use: "create", - Short: "Create a folder", - Run: func(cmd *cobra.Command, args []string) { - environmentName, _ := cmd.Flags().GetString("env") - if !cmd.Flags().Changed("env") { - environmentFromWorkspace := util.GetEnvFromWorkspaceFile() - if environmentFromWorkspace != "" { - environmentName = environmentFromWorkspace - } - } - - token, err := util.GetInfisicalToken(cmd) - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - projectId, err := cmd.Flags().GetString("projectId") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - folderPath, err := cmd.Flags().GetString("path") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - folderName, err := cmd.Flags().GetString("name") - if err != nil { - util.HandleError(err, "Unable to parse name flag") - } - - if folderName == "" { - util.HandleError(errors.New("invalid folder name, folder name cannot be empty")) - } - - if err != nil { - util.HandleError(err, "Unable to get workspace file") - } - - if projectId == "" { - workspaceFile, err := util.GetWorkSpaceFromFile() - if err != nil { - util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag") - } - - projectId = workspaceFile.WorkspaceId - } - - params := models.CreateFolderParameters{ - FolderName: folderName, - Environment: environmentName, - FolderPath: folderPath, - WorkspaceId: projectId, - } - - if token != nil && (token.Type == util.SERVICE_TOKEN_IDENTIFIER || token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER) { - params.InfisicalToken = token.Token - } - - _, err = util.CreateFolder(params) - if err != nil { - util.HandleError(err, "Unable to create folder") - } - - util.PrintSuccessMessage(fmt.Sprintf("folder named `%s` created in path %s", folderName, folderPath)) - - Telemetry.CaptureEvent("cli-command:folders create", posthog.NewProperties().Set("version", util.CLI_VERSION)) - }, -} - -var deleteCmd = &cobra.Command{ - Use: "delete", - Short: "Delete a folder", - Run: func(cmd *cobra.Command, args []string) { - - environmentName, _ := cmd.Flags().GetString("env") - if !cmd.Flags().Changed("env") { - environmentFromWorkspace := util.GetEnvFromWorkspaceFile() - if environmentFromWorkspace != "" { - environmentName = environmentFromWorkspace - } - } - - token, err := util.GetInfisicalToken(cmd) - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - projectId, err := cmd.Flags().GetString("projectId") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - folderPath, err := cmd.Flags().GetString("path") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - folderName, err := cmd.Flags().GetString("name") - if err != nil { - util.HandleError(err, "Unable to parse name flag") - } - - if folderName == "" { - util.HandleError(errors.New("invalid folder name, folder name cannot be empty")) - } - - if projectId == "" { - workspaceFile, err := util.GetWorkSpaceFromFile() - if err != nil { - util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag") - } - - projectId = workspaceFile.WorkspaceId - } - - params := models.DeleteFolderParameters{ - FolderName: folderName, - WorkspaceId: projectId, - Environment: environmentName, - FolderPath: folderPath, - } - - if token != nil && (token.Type == util.SERVICE_TOKEN_IDENTIFIER || token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER) { - params.InfisicalToken = token.Token - } - - _, err = util.DeleteFolder(params) - if err != nil { - util.HandleError(err, "Unable to delete folder") - } - - util.PrintSuccessMessage(fmt.Sprintf("folder named `%s` deleted in path %s", folderName, folderPath)) - - Telemetry.CaptureEvent("cli-command:folders delete", posthog.NewProperties().Set("version", util.CLI_VERSION)) - }, -} diff --git a/cli/packages/cmd/gateway.go b/cli/packages/cmd/gateway.go deleted file mode 100644 index abc4d6949..000000000 --- a/cli/packages/cmd/gateway.go +++ /dev/null @@ -1,318 +0,0 @@ -package cmd - -import ( - "context" - "fmt" - "os" - "os/exec" - "os/signal" - "runtime" - "sync/atomic" - "syscall" - "time" - - "github.com/Infisical/infisical-merge/packages/api" - "github.com/Infisical/infisical-merge/packages/config" - "github.com/Infisical/infisical-merge/packages/gateway" - "github.com/Infisical/infisical-merge/packages/util" - infisicalSdk "github.com/infisical/go-sdk" - "github.com/pkg/errors" - "github.com/posthog/posthog-go" - "github.com/rs/zerolog/log" - "github.com/spf13/cobra" -) - -func getInfisicalSdkInstance(cmd *cobra.Command) (infisicalSdk.InfisicalClientInterface, context.CancelFunc, error) { - - ctx, cancel := context.WithCancel(cmd.Context()) - infisicalClient := infisicalSdk.NewInfisicalClient(ctx, infisicalSdk.Config{ - SiteUrl: config.INFISICAL_URL, - UserAgent: api.USER_AGENT, - }) - - token, err := util.GetInfisicalToken(cmd) - if err != nil { - cancel() - return nil, nil, err - } - - // if the --token param is set, we use it directly for authentication - if token != nil { - infisicalClient.Auth().SetAccessToken(token.Token) - return infisicalClient, cancel, nil - } - - // if the --token param is not set, we use the auth-method flag to determine the authentication method, and perform the appropriate login flow based on that - authMethod, err := util.GetCmdFlagOrEnv(cmd, "auth-method", []string{util.INFISICAL_AUTH_METHOD_NAME}) - - if err != nil { - cancel() - return nil, nil, err - } - - authMethodValid, strategy := util.IsAuthMethodValid(authMethod, false) - if !authMethodValid { - util.PrintErrorMessageAndExit(fmt.Sprintf("Invalid login method: %s", authMethod)) - } - - sdkAuthenticator := util.NewSdkAuthenticator(infisicalClient, cmd) - - authStrategies := map[util.AuthStrategyType]func() (credential infisicalSdk.MachineIdentityCredential, e error){ - util.AuthStrategy.UNIVERSAL_AUTH: sdkAuthenticator.HandleUniversalAuthLogin, - util.AuthStrategy.KUBERNETES_AUTH: sdkAuthenticator.HandleKubernetesAuthLogin, - util.AuthStrategy.AZURE_AUTH: sdkAuthenticator.HandleAzureAuthLogin, - util.AuthStrategy.GCP_ID_TOKEN_AUTH: sdkAuthenticator.HandleGcpIdTokenAuthLogin, - util.AuthStrategy.GCP_IAM_AUTH: sdkAuthenticator.HandleGcpIamAuthLogin, - util.AuthStrategy.AWS_IAM_AUTH: sdkAuthenticator.HandleAwsIamAuthLogin, - util.AuthStrategy.OIDC_AUTH: sdkAuthenticator.HandleOidcAuthLogin, - util.AuthStrategy.JWT_AUTH: sdkAuthenticator.HandleJwtAuthLogin, - } - - _, err = authStrategies[strategy]() - - if err != nil { - cancel() - return nil, nil, err - } - - return infisicalClient, cancel, nil -} - -var gatewayCmd = &cobra.Command{ - Use: "gateway", - Short: "Run the Infisical gateway or manage its systemd service", - Long: "Run the Infisical gateway in the foreground or manage its systemd service installation. Use 'gateway install' to set up the systemd service.", - Example: `infisical gateway --token= - sudo infisical gateway install --token= --domain=`, - DisableFlagsInUseLine: true, - Args: cobra.NoArgs, - Run: func(cmd *cobra.Command, args []string) { - - infisicalClient, cancelSdk, err := getInfisicalSdkInstance(cmd) - if err != nil { - util.HandleError(err, "unable to get infisical client") - } - defer cancelSdk() - - var accessToken atomic.Value - accessToken.Store(infisicalClient.Auth().GetAccessToken()) - - if accessToken.Load().(string) == "" { - util.HandleError(errors.New("no access token found")) - } - - Telemetry.CaptureEvent("cli-command:gateway", posthog.NewProperties().Set("version", util.CLI_VERSION)) - - sigCh := make(chan os.Signal, 1) - signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) - sigStopCh := make(chan bool, 1) - - ctx, cancelCmd := context.WithCancel(cmd.Context()) - defer cancelCmd() - - go func() { - <-sigCh - close(sigStopCh) - cancelCmd() - cancelSdk() - - // If we get a second signal, force exit - <-sigCh - log.Warn().Msgf("Force exit triggered") - os.Exit(1) - }() - - var gatewayInstance *gateway.Gateway - - // Token refresh goroutine - runs every 10 seconds - go func() { - tokenRefreshTicker := time.NewTicker(10 * time.Second) - defer tokenRefreshTicker.Stop() - - for { - select { - case <-tokenRefreshTicker.C: - if ctx.Err() != nil { - return - } - - newToken := infisicalClient.Auth().GetAccessToken() - if newToken != "" && newToken != accessToken.Load().(string) { - accessToken.Store(newToken) - if gatewayInstance != nil { - gatewayInstance.UpdateIdentityAccessToken(newToken) - } - } - - case <-ctx.Done(): - return - } - } - }() - - // Main gateway retry loop with proper context handling - retryTicker := time.NewTicker(5 * time.Second) - defer retryTicker.Stop() - - for { - if ctx.Err() != nil { - log.Info().Msg("Shutting down gateway") - return - } - gatewayInstance, err := gateway.NewGateway(accessToken.Load().(string)) - if err != nil { - util.HandleError(err) - } - - if err = gatewayInstance.ConnectWithRelay(); err != nil { - if ctx.Err() != nil { - log.Info().Msg("Shutting down gateway") - return - } - - log.Error().Msgf("Gateway connection error with relay: %s", err) - log.Info().Msg("Retrying connection in 5 seconds...") - select { - case <-retryTicker.C: - continue - case <-ctx.Done(): - log.Info().Msg("Shutting down gateway") - return - } - } - - err = gatewayInstance.Listen(ctx) - if ctx.Err() != nil { - log.Info().Msg("Gateway shutdown complete") - return - } - log.Error().Msgf("Gateway listen error: %s", err) - log.Info().Msg("Retrying connection in 5 seconds...") - select { - case <-retryTicker.C: - continue - case <-ctx.Done(): - log.Info().Msg("Shutting down gateway") - return - } - } - }, -} - -var gatewayInstallCmd = &cobra.Command{ - Use: "install", - Short: "Install and enable systemd service for the gateway (requires sudo)", - Long: "Install and enable systemd service for the gateway. Must be run with sudo on Linux.", - Example: "sudo infisical gateway install --token= --domain=", - DisableFlagsInUseLine: true, - Args: cobra.NoArgs, - Run: func(cmd *cobra.Command, args []string) { - if runtime.GOOS != "linux" { - util.HandleError(fmt.Errorf("systemd service installation is only supported on Linux")) - } - - if os.Geteuid() != 0 { - util.HandleError(fmt.Errorf("systemd service installation requires root/sudo privileges")) - } - - token, err := util.GetInfisicalToken(cmd) - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - if token == nil { - util.HandleError(errors.New("Token not found")) - } - - domain, err := cmd.Flags().GetString("domain") - if err != nil { - util.HandleError(err, "Unable to parse domain flag") - } - - if err := gateway.InstallGatewaySystemdService(token.Token, domain); err != nil { - util.HandleError(err, "Failed to install systemd service") - } - - enableCmd := exec.Command("systemctl", "enable", "infisical-gateway") - if err := enableCmd.Run(); err != nil { - util.HandleError(err, "Failed to enable systemd service") - } - - log.Info().Msg("Successfully installed and enabled infisical-gateway service") - log.Info().Msg("To start the service, run: sudo systemctl start infisical-gateway") - }, -} - -var gatewayUninstallCmd = &cobra.Command{ - Use: "uninstall", - Short: "Uninstall and remove systemd service for the gateway (requires sudo)", - Long: "Uninstall and remove systemd service for the gateway. Must be run with sudo on Linux.", - Example: "sudo infisical gateway uninstall", - DisableFlagsInUseLine: true, - Args: cobra.NoArgs, - Run: func(cmd *cobra.Command, args []string) { - if runtime.GOOS != "linux" { - util.HandleError(fmt.Errorf("systemd service installation is only supported on Linux")) - } - - if os.Geteuid() != 0 { - util.HandleError(fmt.Errorf("systemd service installation requires root/sudo privileges")) - } - - if err := gateway.UninstallGatewaySystemdService(); err != nil { - util.HandleError(err, "Failed to uninstall systemd service") - } - }, -} - -var gatewayRelayCmd = &cobra.Command{ - Example: `infisical gateway relay`, - Short: "Used to run infisical gateway relay", - Use: "relay", - DisableFlagsInUseLine: true, - Args: cobra.NoArgs, - Run: func(cmd *cobra.Command, args []string) { - relayConfigFilePath, err := cmd.Flags().GetString("config") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - if relayConfigFilePath == "" { - util.HandleError(errors.New("Missing config file")) - } - - gatewayRelay, err := gateway.NewGatewayRelay(relayConfigFilePath) - if err != nil { - util.HandleError(err, "Failed to initialize gateway") - } - err = gatewayRelay.Run() - if err != nil { - util.HandleError(err, "Failed to start gateway") - } - }, -} - -func init() { - gatewayCmd.Flags().String("token", "", "connect with Infisical using machine identity access token. if not provided, you must set the auth-method flag") - - gatewayCmd.Flags().String("auth-method", "", "login method [universal-auth, kubernetes, azure, gcp-id-token, gcp-iam, aws-iam, oidc-auth]. if not provided, you must set the token flag") - - gatewayCmd.Flags().String("client-id", "", "client id for universal auth") - gatewayCmd.Flags().String("client-secret", "", "client secret for universal auth") - - gatewayCmd.Flags().String("machine-identity-id", "", "machine identity id for kubernetes, azure, gcp-id-token, gcp-iam, and aws-iam auth methods") - gatewayCmd.Flags().String("service-account-token-path", "", "service account token path for kubernetes auth") - gatewayCmd.Flags().String("service-account-key-file-path", "", "service account key file path for GCP IAM auth") - - gatewayCmd.Flags().String("jwt", "", "JWT for jwt-based auth methods [oidc-auth, jwt-auth]") - - gatewayInstallCmd.Flags().String("token", "", "Connect with Infisical using machine identity access token") - gatewayInstallCmd.Flags().String("domain", "", "Domain of your self-hosted Infisical instance") - - gatewayRelayCmd.Flags().String("config", "", "Relay config yaml file path") - - gatewayCmd.AddCommand(gatewayInstallCmd) - gatewayCmd.AddCommand(gatewayUninstallCmd) - gatewayCmd.AddCommand(gatewayRelayCmd) - rootCmd.AddCommand(gatewayCmd) -} diff --git a/cli/packages/cmd/init.go b/cli/packages/cmd/init.go deleted file mode 100644 index 2ef555a82..000000000 --- a/cli/packages/cmd/init.go +++ /dev/null @@ -1,195 +0,0 @@ -/* -Copyright (c) 2023 Infisical Inc. -*/ -package cmd - -import ( - "encoding/json" - "fmt" - - "github.com/Infisical/infisical-merge/packages/api" - "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/rs/zerolog/log" - "github.com/spf13/cobra" -) - -// runCmd represents the run command -var initCmd = &cobra.Command{ - Use: "init", - Short: "Used to connect your local project with Infisical project", - DisableFlagsInUseLine: true, - Example: "infisical init", - Args: cobra.ExactArgs(0), - PreRun: func(cmd *cobra.Command, args []string) { - util.RequireLogin() - }, - Run: func(cmd *cobra.Command, args []string) { - if util.WorkspaceConfigFileExistsInCurrentPath() { - shouldOverride, err := shouldOverrideWorkspacePrompt() - if err != nil { - log.Error().Msg("Unable to parse your answer") - log.Debug().Err(err) - return - } - - if !shouldOverride { - return - } - } - - userCreds, err := util.GetCurrentLoggedInUserDetails(true) - if err != nil { - util.HandleError(err, "Unable to get your login details") - } - - if userCreds.LoginExpired { - userCreds = util.EstablishUserLoginSession() - } - - httpClient, err := util.GetRestyClientWithCustomHeaders() - if err != nil { - util.HandleError(err, "Unable to get resty client with custom headers") - } - httpClient.SetAuthToken(userCreds.UserCredentials.JTWToken) - - organizationResponse, err := api.CallGetAllOrganizations(httpClient) - if err != nil { - util.HandleError(err, "Unable to pull organizations that belong to you") - } - - organizations := organizationResponse.Organizations - - organizationNames := util.GetOrganizationsNameList(organizationResponse) - - prompt := promptui.Select{ - Label: "Which Infisical organization would you like to select a project from?", - Items: organizationNames, - Size: 7, - } - - index, _, err := prompt.Run() - if err != nil { - util.HandleError(err) - } - - selectedOrganization := organizations[index] - - tokenResponse, err := api.CallSelectOrganization(httpClient, api.SelectOrganizationRequest{OrganizationId: selectedOrganization.ID}) - if tokenResponse.MfaEnabled { - i := 1 - for i < 6 { - mfaVerifyCode := askForMFACode(tokenResponse.MfaMethod) - - httpClient, err := util.GetRestyClientWithCustomHeaders() - if err != nil { - util.HandleError(err, "Unable to get resty client with custom headers") - } - httpClient.SetAuthToken(tokenResponse.Token) - verifyMFAresponse, mfaErrorResponse, requestError := api.CallVerifyMfaToken(httpClient, api.VerifyMfaTokenRequest{ - Email: userCreds.UserCredentials.Email, - MFAToken: mfaVerifyCode, - MFAMethod: tokenResponse.MfaMethod, - }) - if requestError != nil { - util.HandleError(err) - break - } else if mfaErrorResponse != nil { - if mfaErrorResponse.Context.Code == "mfa_invalid" { - msg := fmt.Sprintf("Incorrect, verification code. You have %v attempts left", 5-i) - fmt.Println(msg) - if i == 5 { - util.PrintErrorMessageAndExit("No tries left, please try again in a bit") - break - } - } - - if mfaErrorResponse.Context.Code == "mfa_expired" { - util.PrintErrorMessageAndExit("Your 2FA verification code has expired, please try logging in again") - break - } - i++ - } else { - httpClient.SetAuthToken(verifyMFAresponse.Token) - tokenResponse, err = api.CallSelectOrganization(httpClient, api.SelectOrganizationRequest{OrganizationId: selectedOrganization.ID}) - break - } - } - } - - if err != nil { - util.HandleError(err, "Unable to select organization") - } - - // set the config jwt token to the new token - userCreds.UserCredentials.JTWToken = tokenResponse.Token - err = util.StoreUserCredsInKeyRing(&userCreds.UserCredentials) - httpClient.SetAuthToken(tokenResponse.Token) - - if err != nil { - util.HandleError(err, "Unable to store your user credentials") - } - - workspaceResponse, err := api.CallGetAllWorkSpacesUserBelongsTo(httpClient) - if err != nil { - util.HandleError(err, "Unable to pull projects that belong to you") - } - - filteredWorkspaces, workspaceNames := util.GetWorkspacesInOrganization(workspaceResponse, selectedOrganization.ID) - - prompt = promptui.Select{ - Label: "Which of your Infisical projects would you like to connect this project to?", - Items: workspaceNames, - Size: 7, - } - - index, _, err = prompt.Run() - if err != nil { - util.HandleError(err) - } - - err = writeWorkspaceFile(filteredWorkspaces[index]) - if err != nil { - util.HandleError(err) - } - - Telemetry.CaptureEvent("cli-command:init", posthog.NewProperties().Set("version", util.CLI_VERSION)) - - }, -} - -func init() { - rootCmd.AddCommand(initCmd) -} - -func writeWorkspaceFile(selectedWorkspace models.Workspace) error { - workspaceFileToSave := models.WorkspaceConfigFile{ - WorkspaceId: selectedWorkspace.ID, - } - - marshalledWorkspaceFile, err := json.MarshalIndent(workspaceFileToSave, "", " ") - if err != nil { - return err - } - - err = util.WriteToFile(util.INFISICAL_WORKSPACE_CONFIG_FILE_NAME, marshalledWorkspaceFile, 0600) - if err != nil { - return err - } - - return nil -} - -func shouldOverrideWorkspacePrompt() (bool, error) { - prompt := promptui.Select{ - Label: "A workspace config file already exists here. Would you like to override? Select[Yes/No]", - Items: []string{"No", "Yes"}, - } - _, result, err := prompt.Run() - if err != nil { - return false, err - } - return result == "Yes", nil -} diff --git a/cli/packages/cmd/kmip.go b/cli/packages/cmd/kmip.go deleted file mode 100644 index 91335d122..000000000 --- a/cli/packages/cmd/kmip.go +++ /dev/null @@ -1,103 +0,0 @@ -/* -Copyright (c) 2023 Infisical Inc. -*/ -package cmd - -import ( - "fmt" - - "github.com/Infisical/infisical-merge/packages/config" - "github.com/Infisical/infisical-merge/packages/util" - kmip "github.com/infisical/infisical-kmip" - "github.com/spf13/cobra" -) - -var kmipCmd = &cobra.Command{ - Example: `infisical kmip`, - Short: "Used to manage KMIP servers", - Use: "kmip", - DisableFlagsInUseLine: true, - Args: cobra.NoArgs, -} - -var kmipStartCmd = &cobra.Command{ - Example: `infisical kmip start`, - Short: "Used to start a KMIP server", - Use: "start", - DisableFlagsInUseLine: true, - Args: cobra.NoArgs, - Run: startKmipServer, -} - -func startKmipServer(cmd *cobra.Command, args []string) { - listenAddr, err := cmd.Flags().GetString("listen-address") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - identityAuthMethod, err := cmd.Flags().GetString("identity-auth-method") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - authMethodValid, strategy := util.IsAuthMethodValid(identityAuthMethod, false) - if !authMethodValid { - util.PrintErrorMessageAndExit(fmt.Sprintf("Invalid login method: %s", identityAuthMethod)) - } - - var identityClientId string - var identityClientSecret string - - if strategy == util.AuthStrategy.UNIVERSAL_AUTH { - identityClientId, err = util.GetCmdFlagOrEnv(cmd, "identity-client-id", []string{util.INFISICAL_UNIVERSAL_AUTH_CLIENT_ID_NAME}) - - if err != nil { - util.HandleError(err, "Unable to parse identity client ID") - } - - identityClientSecret, err = util.GetCmdFlagOrEnv(cmd, "identity-client-secret", []string{util.INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET_NAME}) - if err != nil { - util.HandleError(err, "Unable to parse identity client secret") - } - } else { - util.PrintErrorMessageAndExit(fmt.Sprintf("Unsupported login method: %s", identityAuthMethod)) - } - - serverName, err := cmd.Flags().GetString("server-name") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - certificateTTL, err := cmd.Flags().GetString("certificate-ttl") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - hostnamesOrIps, err := cmd.Flags().GetString("hostnames-or-ips") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - kmip.StartServer(kmip.ServerConfig{ - Addr: listenAddr, - InfisicalBaseAPIURL: config.INFISICAL_URL, - IdentityClientId: identityClientId, - IdentityClientSecret: identityClientSecret, - ServerName: serverName, - CertificateTTL: certificateTTL, - HostnamesOrIps: hostnamesOrIps, - }) -} - -func init() { - kmipStartCmd.Flags().String("listen-address", "localhost:5696", "The address for the KMIP server to listen on. Defaults to localhost:5696") - kmipStartCmd.Flags().String("identity-auth-method", string(util.AuthStrategy.UNIVERSAL_AUTH), "The auth method to use for authenticating the machine identity. Defaults to universal-auth.") - kmipStartCmd.Flags().String("identity-client-id", "", "Universal auth client ID of machine identity") - kmipStartCmd.Flags().String("identity-client-secret", "", "Universal auth client secret of machine identity") - kmipStartCmd.Flags().String("server-name", "kmip-server", "The name of the KMIP server") - kmipStartCmd.Flags().String("certificate-ttl", "1y", "The TTL duration for the server certificate") - kmipStartCmd.Flags().String("hostnames-or-ips", "", "Comma-separated list of hostnames or IPs") - - kmipCmd.AddCommand(kmipStartCmd) - rootCmd.AddCommand(kmipCmd) -} diff --git a/cli/packages/cmd/login.go b/cli/packages/cmd/login.go deleted file mode 100644 index fd3ce1569..000000000 --- a/cli/packages/cmd/login.go +++ /dev/null @@ -1,1022 +0,0 @@ -/* -Copyright (c) 2023 Infisical Inc. -*/ -package cmd - -import ( - "context" - "encoding/base64" - "encoding/hex" - "encoding/json" - "os" - "runtime" - "slices" - "strings" - "time" - - "errors" - "fmt" - "net" - "net/http" - "net/url" - "regexp" - - browser "github.com/pkg/browser" - - "github.com/Infisical/infisical-merge/packages/api" - "github.com/Infisical/infisical-merge/packages/config" - "github.com/Infisical/infisical-merge/packages/crypto" - "github.com/Infisical/infisical-merge/packages/models" - "github.com/Infisical/infisical-merge/packages/srp" - "github.com/Infisical/infisical-merge/packages/util" - "github.com/fatih/color" - "github.com/manifoldco/promptui" - "github.com/posthog/posthog-go" - "github.com/rs/cors" - "github.com/rs/zerolog/log" - "github.com/spf13/cobra" - "golang.org/x/crypto/argon2" - "golang.org/x/term" - - infisicalSdk "github.com/infisical/go-sdk" -) - -type params struct { - memory uint32 - iterations uint32 - parallelism uint8 - saltLength uint32 - keyLength uint32 -} - -func formatAuthMethod(authMethod string) string { - return strings.ReplaceAll(authMethod, "-", " ") -} - -const ADD_USER = "Add a new account login" -const REPLACE_USER = "Override current logged in user" -const EXIT_USER_MENU = "Exit" -const QUIT_BROWSER_LOGIN = "q" - -// loginCmd represents the login command -var loginCmd = &cobra.Command{ - Use: "login", - Short: "Login into your Infisical account", - DisableFlagsInUseLine: true, - PreRunE: func(cmd *cobra.Command, args []string) error { - // daniel: oidc-jwt is deprecated in favor of `jwt`. we backfill the `jwt` flag with the value of `oidc-jwt` if it's set. - if cmd.Flags().Changed("oidc-jwt") && !cmd.Flags().Changed("jwt") { - oidcJWT, err := cmd.Flags().GetString("oidc-jwt") - if err != nil { - return err - } - - err = cmd.Flags().Set("jwt", oidcJWT) - if err != nil { - return err - } - } - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - presetDomain := config.INFISICAL_URL - - clearSelfHostedDomains, err := cmd.Flags().GetBool("clear-domains") - if err != nil { - util.HandleError(err) - } - - if clearSelfHostedDomains { - infisicalConfig, err := util.GetConfigFile() - if err != nil { - util.HandleError(err) - } - - infisicalConfig.Domains = []string{} - err = util.WriteConfigFile(&infisicalConfig) - - if err != nil { - util.HandleError(err) - } - - fmt.Println("Cleared all self-hosted domains from the config file") - return - } - - customHeaders, err := util.GetInfisicalCustomHeadersMap() - if err != nil { - util.HandleError(err, "Unable to get custom headers") - } - - infisicalClient := infisicalSdk.NewInfisicalClient(context.Background(), infisicalSdk.Config{ - SiteUrl: config.INFISICAL_URL, - UserAgent: api.USER_AGENT, - AutoTokenRefresh: false, - CustomHeaders: customHeaders, - }) - - loginMethod, err := cmd.Flags().GetString("method") - if err != nil { - util.HandleError(err) - } - plainOutput, err := cmd.Flags().GetBool("plain") - if err != nil { - util.HandleError(err) - } - - authMethodValid, strategy := util.IsAuthMethodValid(loginMethod, true) - if !authMethodValid { - util.PrintErrorMessageAndExit(fmt.Sprintf("Invalid login method: %s", loginMethod)) - } - - // standalone user auth - if loginMethod == "user" { - currentLoggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true) - // if the key can't be found or there is an error getting current credentials from key ring, allow them to override - if err != nil && (strings.Contains(err.Error(), "we couldn't find your logged in details")) { - log.Debug().Err(err) - } else if err != nil { - util.HandleError(err) - } - - if currentLoggedInUserDetails.IsUserLoggedIn && !currentLoggedInUserDetails.LoginExpired && len(currentLoggedInUserDetails.UserCredentials.PrivateKey) != 0 { - shouldOverride, err := userLoginMenu(currentLoggedInUserDetails.UserCredentials.Email) - if err != nil { - util.HandleError(err) - } - - if !shouldOverride { - return - } - } - - usePresetDomain, err := usePresetDomain(presetDomain) - - if err != nil { - util.HandleError(err) - } - - //override domain - domainQuery := true - if config.INFISICAL_URL_MANUAL_OVERRIDE != "" && - config.INFISICAL_URL_MANUAL_OVERRIDE != fmt.Sprintf("%s/api", util.INFISICAL_DEFAULT_EU_URL) && - config.INFISICAL_URL_MANUAL_OVERRIDE != fmt.Sprintf("%s/api", util.INFISICAL_DEFAULT_US_URL) && - !usePresetDomain { - overrideDomain, err := DomainOverridePrompt() - if err != nil { - util.HandleError(err) - } - - //if not override set INFISICAL_URL to exported var - //set domainQuery to false - if !overrideDomain && !usePresetDomain { - domainQuery = false - config.INFISICAL_URL = util.AppendAPIEndpoint(config.INFISICAL_URL_MANUAL_OVERRIDE) - config.INFISICAL_LOGIN_URL = fmt.Sprintf("%s/login", strings.TrimSuffix(config.INFISICAL_URL, "/api")) - } - - } - - //prompt user to select domain between Infisical cloud and self-hosting - if domainQuery && !usePresetDomain { - err = askForDomain() - if err != nil { - util.HandleError(err, "Unable to parse domain url") - } - } - var userCredentialsToBeStored models.UserCredentials - - interactiveLogin := false - if cmd.Flags().Changed("interactive") { - interactiveLogin = true - cliDefaultLogin(&userCredentialsToBeStored) - } - - //call browser login function - if !interactiveLogin { - userCredentialsToBeStored, err = browserCliLogin() - if err != nil { - fmt.Printf("Login via browser failed. %s", err.Error()) - //default to cli login on error - cliDefaultLogin(&userCredentialsToBeStored) - } - } - - err = util.StoreUserCredsInKeyRing(&userCredentialsToBeStored) - if err != nil { - log.Error().Msgf("Unable to store your credentials in system vault") - log.Error().Msgf("\nTo trouble shoot further, read https://infisical.com/docs/cli/faq") - log.Debug().Err(err) - //return here - util.HandleError(err) - } - - err = util.WriteInitalConfig(&userCredentialsToBeStored) - if err != nil { - util.HandleError(err, "Unable to write write to Infisical Config file. Please try again") - } - - // clear backed up secrets from prev account - util.DeleteBackupSecrets() - - whilte := color.New(color.FgGreen) - boldWhite := whilte.Add(color.Bold) - time.Sleep(time.Second * 1) - boldWhite.Printf(">>>> Welcome to Infisical!") - boldWhite.Printf(" You are now logged in as %v <<<< \n", userCredentialsToBeStored.Email) - - plainBold := color.New(color.Bold) - - 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)) - } else { - - sdkAuthenticator := util.NewSdkAuthenticator(infisicalClient, cmd) - - authStrategies := map[util.AuthStrategyType]func() (credential infisicalSdk.MachineIdentityCredential, e error){ - util.AuthStrategy.UNIVERSAL_AUTH: sdkAuthenticator.HandleUniversalAuthLogin, - util.AuthStrategy.KUBERNETES_AUTH: sdkAuthenticator.HandleKubernetesAuthLogin, - util.AuthStrategy.AZURE_AUTH: sdkAuthenticator.HandleAzureAuthLogin, - util.AuthStrategy.GCP_ID_TOKEN_AUTH: sdkAuthenticator.HandleGcpIdTokenAuthLogin, - util.AuthStrategy.GCP_IAM_AUTH: sdkAuthenticator.HandleGcpIamAuthLogin, - util.AuthStrategy.AWS_IAM_AUTH: sdkAuthenticator.HandleAwsIamAuthLogin, - util.AuthStrategy.OIDC_AUTH: sdkAuthenticator.HandleOidcAuthLogin, - util.AuthStrategy.JWT_AUTH: sdkAuthenticator.HandleJwtAuthLogin, - } - - credential, err := authStrategies[strategy]() - - if err != nil { - euErrorMessage := "" - if strings.HasPrefix(config.INFISICAL_URL, util.INFISICAL_DEFAULT_US_URL) { - euErrorMessage = fmt.Sprintf("\nIf you are using the Infisical Cloud Europe Region, please switch to it by using the \"--domain %s\" flag.", util.INFISICAL_DEFAULT_EU_URL) - } - util.HandleError(fmt.Errorf("unable to authenticate with %s [err=%v].%s", formatAuthMethod(loginMethod), err, euErrorMessage)) - } - - if plainOutput { - fmt.Println(credential.AccessToken) - return - } - - boldGreen := color.New(color.FgGreen).Add(color.Bold) - boldPlain := color.New(color.Bold) - time.Sleep(time.Second * 1) - boldGreen.Printf(">>>> Successfully authenticated with %s!\n\n", formatAuthMethod(loginMethod)) - boldPlain.Printf("Access Token:\n%v", credential.AccessToken) - - plainBold := color.New(color.Bold) - plainBold.Println("\n\nYou can use this access token to authenticate through other commands in the CLI.") - - } - }, -} - -func cliDefaultLogin(userCredentialsToBeStored *models.UserCredentials) { - email, password, err := askForLoginCredentials() - if err != nil { - util.HandleError(err, "Unable to parse email and password for authentication") - } - - loginOneResponse, loginTwoResponse, err := getFreshUserCredentials(email, password) - if err != nil { - fmt.Println("Unable to authenticate with the provided credentials, please try again") - log.Debug().Err(err) - //return here - util.HandleError(err) - } - - if loginTwoResponse.MfaEnabled { - i := 1 - for i < 6 { - mfaVerifyCode := askForMFACode("email") - - httpClient, err := util.GetRestyClientWithCustomHeaders() - if err != nil { - util.HandleError(err, "Unable to get resty client with custom headers") - } - httpClient.SetAuthToken(loginTwoResponse.Token) - verifyMFAresponse, mfaErrorResponse, requestError := api.CallVerifyMfaToken(httpClient, api.VerifyMfaTokenRequest{ - Email: email, - MFAToken: mfaVerifyCode, - }) - - if requestError != nil { - util.HandleError(err) - break - } else if mfaErrorResponse != nil { - if mfaErrorResponse.Context.Code == "mfa_invalid" { - msg := fmt.Sprintf("Incorrect, verification code. You have %v attempts left", 5-i) - fmt.Println(msg) - if i == 5 { - util.PrintErrorMessageAndExit("No tries left, please try again in a bit") - break - } - } - - if mfaErrorResponse.Context.Code == "mfa_expired" { - util.PrintErrorMessageAndExit("Your 2FA verification code has expired, please try logging in again") - break - } - i++ - } else { - loginTwoResponse.EncryptedPrivateKey = verifyMFAresponse.EncryptedPrivateKey - loginTwoResponse.EncryptionVersion = verifyMFAresponse.EncryptionVersion - loginTwoResponse.Iv = verifyMFAresponse.Iv - loginTwoResponse.ProtectedKey = verifyMFAresponse.ProtectedKey - loginTwoResponse.ProtectedKeyIV = verifyMFAresponse.ProtectedKeyIV - loginTwoResponse.ProtectedKeyTag = verifyMFAresponse.ProtectedKeyTag - loginTwoResponse.PublicKey = verifyMFAresponse.PublicKey - loginTwoResponse.Tag = verifyMFAresponse.Tag - loginTwoResponse.Token = verifyMFAresponse.Token - loginTwoResponse.EncryptionVersion = verifyMFAresponse.EncryptionVersion - - break - } - } - } - - var decryptedPrivateKey []byte - - if loginTwoResponse.EncryptionVersion == 1 { - log.Debug().Msg("Login version 1") - encryptedPrivateKey, _ := base64.StdEncoding.DecodeString(loginTwoResponse.EncryptedPrivateKey) - tag, err := base64.StdEncoding.DecodeString(loginTwoResponse.Tag) - if err != nil { - util.HandleError(err) - } - - IV, err := base64.StdEncoding.DecodeString(loginTwoResponse.Iv) - if err != nil { - util.HandleError(err) - } - - paddedPassword := fmt.Sprintf("%032s", password) - key := []byte(paddedPassword) - - computedDecryptedPrivateKey, err := crypto.DecryptSymmetric(key, encryptedPrivateKey, tag, IV) - if err != nil || len(computedDecryptedPrivateKey) == 0 { - util.HandleError(err) - } - - decryptedPrivateKey = computedDecryptedPrivateKey - - } else if loginTwoResponse.EncryptionVersion == 2 { - log.Debug().Msg("Login version 2") - protectedKey, err := base64.StdEncoding.DecodeString(loginTwoResponse.ProtectedKey) - if err != nil { - util.HandleError(err) - } - - protectedKeyTag, err := base64.StdEncoding.DecodeString(loginTwoResponse.ProtectedKeyTag) - if err != nil { - util.HandleError(err) - } - - protectedKeyIV, err := base64.StdEncoding.DecodeString(loginTwoResponse.ProtectedKeyIV) - if err != nil { - util.HandleError(err) - } - - nonProtectedTag, err := base64.StdEncoding.DecodeString(loginTwoResponse.Tag) - if err != nil { - util.HandleError(err) - } - - nonProtectedIv, err := base64.StdEncoding.DecodeString(loginTwoResponse.Iv) - if err != nil { - util.HandleError(err) - } - - parameters := ¶ms{ - memory: 64 * 1024, - iterations: 3, - parallelism: 1, - keyLength: 32, - } - - derivedKey, err := generateFromPassword(password, []byte(loginOneResponse.Salt), parameters) - if err != nil { - util.HandleError(fmt.Errorf("unable to generate argon hash from password [err=%s]", err)) - } - - decryptedProtectedKey, err := crypto.DecryptSymmetric(derivedKey, protectedKey, protectedKeyTag, protectedKeyIV) - if err != nil { - util.HandleError(fmt.Errorf("unable to get decrypted protected key [err=%s]", err)) - } - - encryptedPrivateKey, err := base64.StdEncoding.DecodeString(loginTwoResponse.EncryptedPrivateKey) - if err != nil { - util.HandleError(err) - } - - decryptedProtectedKeyInHex, err := hex.DecodeString(string(decryptedProtectedKey)) - if err != nil { - util.HandleError(err) - } - - computedDecryptedPrivateKey, err := crypto.DecryptSymmetric(decryptedProtectedKeyInHex, encryptedPrivateKey, nonProtectedTag, nonProtectedIv) - if err != nil { - util.HandleError(err) - } - - decryptedPrivateKey = computedDecryptedPrivateKey - } else { - util.PrintErrorMessageAndExit("Insufficient details to decrypt private key") - } - - if string(decryptedPrivateKey) == "" || email == "" || loginTwoResponse.Token == "" { - log.Debug().Msgf("[decryptedPrivateKey=%s] [email=%s] [loginTwoResponse.Token=%s]", string(decryptedPrivateKey), email, loginTwoResponse.Token) - util.PrintErrorMessageAndExit("We were unable to fetch required details to complete your login. Run with -d to see more info") - } - // Login is successful so ask user to choose organization - newJwtToken := GetJwtTokenWithOrganizationId(loginTwoResponse.Token, email) - - //updating usercredentials - userCredentialsToBeStored.Email = email - userCredentialsToBeStored.PrivateKey = string(decryptedPrivateKey) - userCredentialsToBeStored.JTWToken = newJwtToken -} - -func init() { - rootCmd.AddCommand(loginCmd) - loginCmd.Flags().Bool("clear-domains", false, "clear all self-hosting domains from the config file") - loginCmd.Flags().BoolP("interactive", "i", false, "login via the command line") - loginCmd.Flags().Bool("plain", false, "only output the token without any formatting") - loginCmd.Flags().String("method", "user", "login method [user, universal-auth, kubernetes, azure, gcp-id-token, gcp-iam, aws-iam, oidc-auth]") - loginCmd.Flags().String("client-id", "", "client id for universal auth") - loginCmd.Flags().String("client-secret", "", "client secret for universal auth") - loginCmd.Flags().String("machine-identity-id", "", "machine identity id for kubernetes, azure, gcp-id-token, gcp-iam, and aws-iam auth methods") - loginCmd.Flags().String("service-account-token-path", "", "service account token path for kubernetes auth") - loginCmd.Flags().String("service-account-key-file-path", "", "service account key file path for GCP IAM auth") - loginCmd.Flags().String("jwt", "", "jwt for jwt-based auth methods [oidc-auth, jwt-auth]") - loginCmd.Flags().String("oidc-jwt", "", "JWT for OIDC authentication. Deprecated, use --jwt instead") - - loginCmd.Flags().MarkDeprecated("oidc-jwt", "use --jwt instead") - -} - -func DomainOverridePrompt() (bool, error) { - const ( - PRESET = "Use Domain" - OVERRIDE = "Change Domain" - ) - - options := []string{PRESET, OVERRIDE} - //trim the '/' from the end of the domain url - config.INFISICAL_URL_MANUAL_OVERRIDE = strings.TrimRight(config.INFISICAL_URL_MANUAL_OVERRIDE, "/") - optionsPrompt := promptui.Select{ - Label: fmt.Sprintf("Current INFISICAL_API_URL Domain Override: %s", config.INFISICAL_URL_MANUAL_OVERRIDE), - Items: options, - Size: 2, - } - - _, selectedOption, err := optionsPrompt.Run() - if err != nil { - return false, err - } - - return selectedOption == OVERRIDE, err -} - -func usePresetDomain(presetDomain string) (bool, error) { - infisicalConfig, err := util.GetConfigFile() - if err != nil { - return false, fmt.Errorf("askForDomain: unable to get config file because [err=%s]", err) - } - - preconfiguredUrl := strings.TrimSuffix(presetDomain, "/api") - - if preconfiguredUrl != "" && preconfiguredUrl != util.INFISICAL_DEFAULT_US_URL && preconfiguredUrl != util.INFISICAL_DEFAULT_EU_URL { - parsedDomain := strings.TrimSuffix(strings.Trim(preconfiguredUrl, "/"), "/api") - - _, err := url.ParseRequestURI(parsedDomain) - if err != nil { - return false, errors.New(fmt.Sprintf("Invalid domain URL: '%s'", parsedDomain)) - } - - config.INFISICAL_URL = fmt.Sprintf("%s/api", parsedDomain) - config.INFISICAL_LOGIN_URL = fmt.Sprintf("%s/login", parsedDomain) - - if !slices.Contains(infisicalConfig.Domains, parsedDomain) { - infisicalConfig.Domains = append(infisicalConfig.Domains, parsedDomain) - err = util.WriteConfigFile(&infisicalConfig) - - if err != nil { - return false, fmt.Errorf("askForDomain: unable to write domains to config file because [err=%s]", err) - } - } - - whilte := color.New(color.FgGreen) - boldWhite := whilte.Add(color.Bold) - time.Sleep(time.Second * 1) - boldWhite.Printf("[INFO] Using domain '%s' from domain flag or INFISICAL_API_URL environment variable\n", parsedDomain) - - return true, nil - } - - return false, nil -} - -func askForDomain() error { - - // query user to choose between Infisical cloud or self-hosting - const ( - INFISICAL_CLOUD_US = "Infisical Cloud (US Region)" - INFISICAL_CLOUD_EU = "Infisical Cloud (EU Region)" - SELF_HOSTING = "Self-Hosting or Dedicated Instance" - ADD_NEW_DOMAIN = "Add a new domain" - ) - - options := []string{INFISICAL_CLOUD_US, INFISICAL_CLOUD_EU, SELF_HOSTING} - optionsPrompt := promptui.Select{ - Label: "Select your hosting option", - Items: options, - Size: 3, - } - - _, selectedHostingOption, err := optionsPrompt.Run() - if err != nil { - return err - } - - if selectedHostingOption == INFISICAL_CLOUD_US { - // US cloud option - config.INFISICAL_URL = fmt.Sprintf("%s/api", util.INFISICAL_DEFAULT_US_URL) - config.INFISICAL_LOGIN_URL = fmt.Sprintf("%s/login", util.INFISICAL_DEFAULT_US_URL) - return nil - } else if selectedHostingOption == INFISICAL_CLOUD_EU { - // EU cloud option - config.INFISICAL_URL = fmt.Sprintf("%s/api", util.INFISICAL_DEFAULT_EU_URL) - config.INFISICAL_LOGIN_URL = fmt.Sprintf("%s/login", util.INFISICAL_DEFAULT_EU_URL) - return nil - } - - infisicalConfig, err := util.GetConfigFile() - if err != nil { - return fmt.Errorf("askForDomain: unable to get config file because [err=%s]", err) - } - - if infisicalConfig.Domains != nil && len(infisicalConfig.Domains) > 0 { - // If domains are present in the config, let the user select from the list or select to add a new domain - - items := append(infisicalConfig.Domains, ADD_NEW_DOMAIN) - - prompt := promptui.Select{ - Label: "Which domain would you like to use?", - Items: items, - Size: 5, - } - - _, selectedOption, err := prompt.Run() - if err != nil { - return err - } - - if selectedOption != ADD_NEW_DOMAIN { - config.INFISICAL_URL = fmt.Sprintf("%s/api", selectedOption) - config.INFISICAL_LOGIN_URL = fmt.Sprintf("%s/login", selectedOption) - return nil - - } - - } - - urlValidation := func(input string) error { - _, err := url.ParseRequestURI(input) - if err != nil { - return errors.New("this is an invalid url") - } - return nil - } - - domainPrompt := promptui.Prompt{ - Label: "Domain", - Validate: urlValidation, - Default: "Example - https://my-self-hosted-instance.com", - } - - domain, err := domainPrompt.Run() - if err != nil { - return err - } - - // Trimmed the '/' from the end of the self-hosting url, and set the api & login url - domain = strings.TrimRight(domain, "/") - config.INFISICAL_URL = fmt.Sprintf("%s/api", domain) - config.INFISICAL_LOGIN_URL = fmt.Sprintf("%s/login", domain) - - // Write the new domain to the config file, to allow the user to select it in the future if needed - // First check if infiscialConfig.Domains already includes the domain, if it does, do not add it again - if !slices.Contains(infisicalConfig.Domains, domain) { - infisicalConfig.Domains = append(infisicalConfig.Domains, domain) - err = util.WriteConfigFile(&infisicalConfig) - - if err != nil { - return fmt.Errorf("askForDomain: unable to write domains to config file because [err=%s]", err) - } - } - - return nil -} - -func askForLoginCredentials() (email string, password string, err error) { - validateEmail := func(input string) error { - matched, err := regexp.MatchString("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$", input) - if err != nil || !matched { - return errors.New("this doesn't look like an email address") - } - return nil - } - - fmt.Println("Enter Credentials...") - emailPrompt := promptui.Prompt{ - Label: "Email", - Validate: validateEmail, - } - - userEmail, err := emailPrompt.Run() - - if err != nil { - return "", "", err - } - - validatePassword := func(input string) error { - if len(input) < 1 { - return errors.New("please enter a valid password") - } - return nil - } - - passwordPrompt := promptui.Prompt{ - Label: "Password", - Validate: validatePassword, - Mask: '*', - } - - userPassword, err := passwordPrompt.Run() - - if err != nil { - return "", "", err - } - - return userEmail, userPassword, nil -} - -func getFreshUserCredentials(email string, password string) (*api.GetLoginOneV2Response, *api.GetLoginTwoV2Response, error) { - log.Debug().Msg(fmt.Sprint("getFreshUserCredentials: ", "email", email, "password: ", password)) - httpClient, err := util.GetRestyClientWithCustomHeaders() - if err != nil { - return nil, nil, err - } - httpClient.SetRetryCount(5) - - params := srp.GetParams(4096) - secret1 := srp.GenKey() - srpClient := srp.NewClient(params, []byte(email), []byte(password), secret1) - srpA := hex.EncodeToString(srpClient.ComputeA()) - - // ** Login one - loginOneResponseResult, err := api.CallLogin1V2(httpClient, api.GetLoginOneV2Request{ - Email: email, - ClientPublicKey: srpA, - }) - - if err != nil { - return nil, nil, err - } - - // **** Login 2 - serverPublicKey_bytearray, err := hex.DecodeString(loginOneResponseResult.ServerPublicKey) - if err != nil { - return nil, nil, err - } - - userSalt, err := hex.DecodeString(loginOneResponseResult.Salt) - if err != nil { - return nil, nil, err - } - - srpClient.SetSalt(userSalt, []byte(email), []byte(password)) - srpClient.SetB(serverPublicKey_bytearray) - - srpM1 := srpClient.ComputeM1() - - loginTwoResponseResult, err := api.CallLogin2V2(httpClient, api.GetLoginTwoV2Request{ - Email: email, - ClientProof: hex.EncodeToString(srpM1), - Password: password, - }) - - if err != nil { - util.HandleError(err) - } - - return &loginOneResponseResult, &loginTwoResponseResult, nil -} - -func GetJwtTokenWithOrganizationId(oldJwtToken string, email string) string { - log.Debug().Msg(fmt.Sprint("GetJwtTokenWithOrganizationId: ", "oldJwtToken", oldJwtToken)) - - httpClient, err := util.GetRestyClientWithCustomHeaders() - if err != nil { - util.HandleError(err, "Unable to get resty client with custom headers") - } - httpClient.SetAuthToken(oldJwtToken) - - organizationResponse, err := api.CallGetAllOrganizations(httpClient) - - if err != nil { - util.HandleError(err, "Unable to pull organizations that belong to you") - } - - organizations := organizationResponse.Organizations - - organizationNames := util.GetOrganizationsNameList(organizationResponse) - - prompt := promptui.Select{ - Label: "Which Infisical organization would you like to log into?", - Items: organizationNames, - } - - index, _, err := prompt.Run() - if err != nil { - util.HandleError(err) - } - - selectedOrganization := organizations[index] - - selectedOrgRes, err := api.CallSelectOrganization(httpClient, api.SelectOrganizationRequest{OrganizationId: selectedOrganization.ID}) - if err != nil { - util.HandleError(err) - } - - if selectedOrgRes.MfaEnabled { - i := 1 - for i < 6 { - mfaVerifyCode := askForMFACode(selectedOrgRes.MfaMethod) - - httpClient, err := util.GetRestyClientWithCustomHeaders() - if err != nil { - util.HandleError(err, "Unable to get resty client with custom headers") - } - httpClient.SetAuthToken(selectedOrgRes.Token) - verifyMFAresponse, mfaErrorResponse, requestError := api.CallVerifyMfaToken(httpClient, api.VerifyMfaTokenRequest{ - Email: email, - MFAToken: mfaVerifyCode, - MFAMethod: selectedOrgRes.MfaMethod, - }) - if requestError != nil { - util.HandleError(err) - break - } else if mfaErrorResponse != nil { - if mfaErrorResponse.Context.Code == "mfa_invalid" { - msg := fmt.Sprintf("Incorrect, verification code. You have %v attempts left", 5-i) - fmt.Println(msg) - if i == 5 { - util.PrintErrorMessageAndExit("No tries left, please try again in a bit") - break - } - } - - if mfaErrorResponse.Context.Code == "mfa_expired" { - util.PrintErrorMessageAndExit("Your 2FA verification code has expired, please try logging in again") - break - } - i++ - } else { - httpClient.SetAuthToken(verifyMFAresponse.Token) - selectedOrgRes, err = api.CallSelectOrganization(httpClient, api.SelectOrganizationRequest{OrganizationId: selectedOrganization.ID}) - break - } - } - } - - if err != nil { - util.HandleError(err, "Unable to select organization") - } - - return selectedOrgRes.Token - -} - -func userLoginMenu(currentLoggedInUserEmail string) (bool, error) { - label := fmt.Sprintf("Current logged in user email: %s on domain: %s", currentLoggedInUserEmail, config.INFISICAL_URL) - - prompt := promptui.Select{ - Label: label, - Items: []string{ADD_USER, REPLACE_USER, EXIT_USER_MENU}, - } - _, result, err := prompt.Run() - if err != nil { - return false, err - } - return result != EXIT_USER_MENU, err -} - -func generateFromPassword(password string, salt []byte, p *params) (hash []byte, err error) { - hash = argon2.IDKey([]byte(password), salt, p.iterations, p.memory, p.parallelism, p.keyLength) - return hash, nil -} - -func askForMFACode(mfaMethod string) string { - var label string - if mfaMethod == "totp" { - label = "Enter the verification code from your mobile authenticator app or use a recovery code" - } else { - label = "Enter the 2FA verification code sent to your email" - } - mfaCodePromptUI := promptui.Prompt{ - Label: label, - } - - mfaVerifyCode, err := mfaCodePromptUI.Run() - if err != nil { - util.HandleError(err) - } - - return mfaVerifyCode -} - -func askToPasteJwtToken(success chan models.UserCredentials, failure chan error) { - time.Sleep(time.Second * 5) - fmt.Println("\n\nOnce login is completed via browser, the CLI should be authenticated automatically.") - fmt.Println("However, if browser fails to communicate with the CLI, please paste the token from the browser below.") - - fmt.Print("\n\nToken: ") - bytePassword, err := term.ReadPassword(int(os.Stdin.Fd())) - if err != nil { - failure <- err - fmt.Println("\nError reading input:", err) - os.Exit(1) - } - - infisicalPastedToken := strings.TrimSpace(string(bytePassword)) - - userCredentials, err := decodePastedBase64Token(infisicalPastedToken) - if err != nil { - failure <- err - fmt.Println("Invalid user credentials provided", err) - os.Exit(1) - } - - // verify JTW - httpClient, err := util.GetRestyClientWithCustomHeaders() - if err != nil { - failure <- err - fmt.Println("Error getting resty client with custom headers", err) - os.Exit(1) - } - - httpClient. - SetAuthToken(userCredentials.JTWToken). - SetHeader("Accept", "application/json") - - isAuthenticated := api.CallIsAuthenticated(httpClient) - if !isAuthenticated { - fmt.Println("Invalid user credentials provided", err) - failure <- err - os.Exit(1) - } - - success <- *userCredentials -} - -func decodePastedBase64Token(token string) (*models.UserCredentials, error) { - data, err := base64.StdEncoding.DecodeString(token) - if err != nil { - return nil, err - } - var loginResponse models.UserCredentials - - err = json.Unmarshal(data, &loginResponse) - if err != nil { - return nil, err - } - - return &loginResponse, nil -} - -// Manages the browser login flow. -// Returns a UserCredentials object on success and an error on failure -func browserCliLogin() (models.UserCredentials, error) { - SERVER_TIMEOUT := 10 * 60 - - //create listener - listener, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - return models.UserCredentials{}, err - } - - //get callback port - callbackPort := listener.Addr().(*net.TCPAddr).Port - url := fmt.Sprintf("%s?callback_port=%d", config.INFISICAL_LOGIN_URL, callbackPort) - - defaultPrintStatement := fmt.Sprintf("\n\nTo complete your login, open this address in your browser: %v \n", url) - - if runtime.GOOS == "darwin" || runtime.GOOS == "windows" { - if err := browser.OpenURL(url); err != nil { - fmt.Print(defaultPrintStatement) - } else { - fmt.Printf("\n\nPlease proceed to your browser to complete the login process.\nIf the browser doesn't open automatically, please open this address in your browser: %v \n", url) - } - } else { - fmt.Print(defaultPrintStatement) - } - - //flow channels - success := make(chan models.UserCredentials) - failure := make(chan error) - timeout := time.After(time.Second * time.Duration(SERVER_TIMEOUT)) - - //terminal state - oldState, err := term.GetState(int(os.Stdin.Fd())) - if err != nil { - return models.UserCredentials{}, err - } - - defer restoreTerminal(oldState) - - //create handler - c := cors.New(cors.Options{ - AllowedOrigins: []string{strings.ReplaceAll(config.INFISICAL_LOGIN_URL, "/login", "")}, - AllowCredentials: true, - AllowedMethods: []string{"POST", "OPTIONS"}, - AllowedHeaders: []string{"Content-Type"}, - Debug: false, - }) - corsHandler := c.Handler(browserLoginHandler(success, failure)) - - log.Debug().Msgf("Callback server listening on port %d", callbackPort) - - go http.Serve(listener, corsHandler) - go askToPasteJwtToken(success, failure) - - for { - select { - case loginResponse := <-success: - _ = closeListener(&listener) - fmt.Println("Browser login successful") - return loginResponse, nil - - case err := <-failure: - serverErr := closeListener(&listener) - return models.UserCredentials{}, errors.Join(err, serverErr) - - case <-timeout: - _ = closeListener(&listener) - return models.UserCredentials{}, errors.New("server timeout") - } - } -} - -func restoreTerminal(oldState *term.State) { - term.Restore(int(os.Stdin.Fd()), oldState) -} - -// // listens to 'q' input on terminal and -// // sends 'true' to 'quit' channel -// func quitBrowserLogin(quit chan bool, oState *term.State) { -// oldState, err := term.MakeRaw(int(os.Stdin.Fd())) -// if err != nil { -// return -// } -// *oState = *oldState -// defer restoreTerminal(oldState) -// b := make([]byte, 1) -// for { -// _, _ = os.Stdin.Read(b) -// if string(b) == QUIT_BROWSER_LOGIN { -// quit <- true -// break -// } -// } -// } - -func closeListener(listener *net.Listener) error { - err := (*listener).Close() - if err != nil { - return err - } - log.Debug().Msg("Callback server shutdown successfully") - return nil -} - -func browserLoginHandler(success chan models.UserCredentials, failure chan error) http.HandlerFunc { - - return func(w http.ResponseWriter, r *http.Request) { - var loginResponse models.UserCredentials - - decoder := json.NewDecoder(r.Body) - err := decoder.Decode(&loginResponse) - if err != nil { - failure <- err - } - - w.WriteHeader(http.StatusOK) - success <- loginResponse - - } -} diff --git a/cli/packages/cmd/man.go b/cli/packages/cmd/man.go deleted file mode 100644 index 86d68c8e7..000000000 --- a/cli/packages/cmd/man.go +++ /dev/null @@ -1,35 +0,0 @@ -/* -Copyright (c) 2023 Infisical Inc. -*/ -package cmd - -import ( - "fmt" - "os" - - mcobra "github.com/muesli/mango-cobra" - "github.com/muesli/roff" - "github.com/spf13/cobra" -) - -var manCmd = &cobra.Command{ - Use: "man", - Short: "generates the manpages", - SilenceUsage: true, - DisableFlagsInUseLine: true, - Hidden: true, - Args: cobra.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - manPage, err := mcobra.NewManPage(1, rootCmd) - if err != nil { - return err - } - - _, err = fmt.Fprint(os.Stdout, manPage.Build(roff.NewDocument())) - return err - }, -} - -func init() { - rootCmd.AddCommand(manCmd) -} diff --git a/cli/packages/cmd/pre-commit-script/pre-commit-without-bang.sh b/cli/packages/cmd/pre-commit-script/pre-commit-without-bang.sh deleted file mode 100644 index e47643cc9..000000000 --- a/cli/packages/cmd/pre-commit-script/pre-commit-without-bang.sh +++ /dev/null @@ -1,20 +0,0 @@ - - -# 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 \ No newline at end of file diff --git a/cli/packages/cmd/pre-commit-script/pre-commit.sh b/cli/packages/cmd/pre-commit-script/pre-commit.sh deleted file mode 100644 index f899a1a51..000000000 --- a/cli/packages/cmd/pre-commit-script/pre-commit.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/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 \ No newline at end of file diff --git a/cli/packages/cmd/reset.go b/cli/packages/cmd/reset.go deleted file mode 100644 index dcc8cebe4..000000000 --- a/cli/packages/cmd/reset.go +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright (c) 2023 Infisical Inc. -*/ -package cmd - -import ( - "os" - - "github.com/Infisical/infisical-merge/packages/util" - "github.com/posthog/posthog-go" - "github.com/spf13/cobra" -) - -var resetCmd = &cobra.Command{ - Use: "reset", - Short: "Used to delete all Infisical related data on your machine", - DisableFlagsInUseLine: true, - Example: "infisical reset", - Args: cobra.NoArgs, - Run: func(cmd *cobra.Command, args []string) { - // delete keyring item of current logged in user - configFile, _ := util.GetConfigFile() - - // delete from keyring - util.DeleteValueInKeyring(configFile.LoggedInUserEmail) - - // delete config - _, pathToDir, err := util.GetFullConfigFilePath() - if err != nil { - util.HandleError(err) - } - - os.RemoveAll(pathToDir) - - // delete secrets backup - util.DeleteBackupSecrets() - - util.PrintSuccessMessage("Reset successful") - Telemetry.CaptureEvent("cli-command:reset", posthog.NewProperties().Set("version", util.CLI_VERSION)) - }, -} - -func init() { - rootCmd.AddCommand(resetCmd) -} diff --git a/cli/packages/cmd/root.go b/cli/packages/cmd/root.go deleted file mode 100644 index b9370ad89..000000000 --- a/cli/packages/cmd/root.go +++ /dev/null @@ -1,104 +0,0 @@ -/* -Copyright (c) 2023 Infisical Inc. -*/ -package cmd - -import ( - "fmt" - "os" - "strings" - - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" - "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", - Long: `Infisical is a simple, end-to-end encrypted service that enables teams to sync and manage their environment variables across their development life cycle.`, - CompletionOptions: cobra.CompletionOptions{HiddenDefaultCmd: true}, - Version: util.CLI_VERSION, -} - -// Execute adds all child commands to the root command and sets flags appropriately. -// This is called by main.main(). It only needs to happen once to the rootCmd. -func Execute() { - err := rootCmd.Execute() - if err != nil { - os.Exit(1) - } -} - -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", fmt.Sprintf("%s/api", util.INFISICAL_DEFAULT_US_URL), "Point the CLI to your own backend [can also set via environment variable name: INFISICAL_API_URL]") - rootCmd.PersistentFlags().Bool("silent", false, "Disable output of tip/info messages. Useful when running in scripts or CI/CD pipelines.") - rootCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) { - silent, err := cmd.Flags().GetBool("silent") - if err != nil { - util.HandleError(err) - } - - config.INFISICAL_URL = util.AppendAPIEndpoint(config.INFISICAL_URL) - - // util.DisplayAptInstallationChangeBanner(silent) - if !util.IsRunningInDocker() && !silent { - util.CheckForUpdate() - } - - loggedInDetails, err := util.GetCurrentLoggedInUserDetails(false) - - if !silent && err == nil && loggedInDetails.IsUserLoggedIn && !loggedInDetails.LoginExpired { - token, err := util.GetInfisicalToken(cmd) - - if err == nil && token != nil { - util.PrintWarning(fmt.Sprintf("Your logged-in session is being overwritten by the token provided from the %s.", token.Source)) - } - } - - } - - // if config.INFISICAL_URL is set to the default value, check if INFISICAL_URL is set in the environment - // this is used to allow overrides of the default value - if !rootCmd.Flag("domain").Changed { - if envInfisicalBackendUrl, ok := os.LookupEnv("INFISICAL_API_URL"); ok { - config.INFISICAL_URL = envInfisicalBackendUrl - } - } - - isTelemetryOn, _ := rootCmd.PersistentFlags().GetBool("telemetry") - Telemetry = telemetry.NewTelemetry(isTelemetryOn) -} - -func initLog() { - zerolog.SetGlobalLevel(zerolog.InfoLevel) - ll, err := rootCmd.Flags().GetString("log-level") - if err != nil { - log.Fatal().Msg(err.Error()) - } - switch strings.ToLower(ll) { - case "trace": - zerolog.SetGlobalLevel(zerolog.TraceLevel) - case "debug": - zerolog.SetGlobalLevel(zerolog.DebugLevel) - case "info": - zerolog.SetGlobalLevel(zerolog.InfoLevel) - case "warn": - zerolog.SetGlobalLevel(zerolog.WarnLevel) - case "err", "error": - zerolog.SetGlobalLevel(zerolog.ErrorLevel) - case "fatal": - zerolog.SetGlobalLevel(zerolog.FatalLevel) - default: - zerolog.SetGlobalLevel(zerolog.InfoLevel) - } -} diff --git a/cli/packages/cmd/run.go b/cli/packages/cmd/run.go deleted file mode 100644 index 7f11a3f95..000000000 --- a/cli/packages/cmd/run.go +++ /dev/null @@ -1,491 +0,0 @@ -/* -Copyright (c) 2023 Infisical Inc. -*/ -package cmd - -import ( - "errors" - "fmt" - "os" - "os/exec" - "os/signal" - "runtime" - "strings" - "sync" - "syscall" - "time" - - "github.com/Infisical/infisical-merge/packages/models" - "github.com/Infisical/infisical-merge/packages/util" - "github.com/fatih/color" - "github.com/rs/zerolog/log" - "github.com/spf13/cobra" -) - -var ErrManualSignalInterrupt = errors.New("signal: interrupt") -var watcherWaitGroup = new(sync.WaitGroup) - -// runCmd represents the run command -var runCmd = &cobra.Command{ - Example: ` - infisical run --env=dev -- npm run dev - infisical run --command "first-command && second-command; more-commands..." - `, - Use: "run [any infisical run command flags] -- [your application start command]", - Short: "Used to inject environments variables into your application process", - DisableFlagsInUseLine: true, - Args: func(cmd *cobra.Command, args []string) error { - // Check if the --command flag has been set - commandFlagSet := cmd.Flags().Changed("command") - - // If the --command flag has been set, check if a value was provided - if commandFlagSet { - command := cmd.Flag("command").Value.String() - if command == "" { - return fmt.Errorf("you need to provide a command after the flag --command") - } - - // If the --command flag has been set, args should not be provided - if len(args) > 0 { - return fmt.Errorf("you cannot set any arguments after --command flag. --command only takes a string command") - } - } else { - // If the --command flag has not been set, at least one arg should be provided - if len(args) == 0 { - return fmt.Errorf("at least one argument is required after the run command, received %d", len(args)) - } - } - - return nil - }, - Run: func(cmd *cobra.Command, args []string) { - environmentName, _ := cmd.Flags().GetString("env") - if !cmd.Flags().Changed("env") { - environmentFromWorkspace := util.GetEnvFromWorkspaceFile() - if environmentFromWorkspace != "" { - environmentName = environmentFromWorkspace - } - } - - token, err := util.GetInfisicalToken(cmd) - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - projectConfigDir, err := cmd.Flags().GetString("project-config-dir") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - projectId, err := cmd.Flags().GetString("projectId") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - command, err := cmd.Flags().GetString("command") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - secretOverriding, err := cmd.Flags().GetBool("secret-overriding") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - watchMode, err := cmd.Flags().GetBool("watch") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - watchModeInterval, err := cmd.Flags().GetInt("watch-interval") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - // If the --watch flag has been set, the --watch-interval flag should also be set - if watchMode && watchModeInterval < 5 { - util.HandleError(fmt.Errorf("watch interval must be at least 5 seconds, you passed %d seconds", watchModeInterval)) - } - - shouldExpandSecrets, err := cmd.Flags().GetBool("expand") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - tagSlugs, err := cmd.Flags().GetString("tags") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - secretsPath, err := cmd.Flags().GetString("path") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - includeImports, err := cmd.Flags().GetBool("include-imports") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - recursive, err := cmd.Flags().GetBool("recursive") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - request := models.GetAllSecretsParameters{ - Environment: environmentName, - WorkspaceId: projectId, - TagSlugs: tagSlugs, - SecretsPath: secretsPath, - IncludeImport: includeImports, - Recursive: recursive, - ExpandSecretReferences: shouldExpandSecrets, - } - - injectableEnvironment, err := fetchAndFormatSecretsForShell(request, projectConfigDir, secretOverriding, token) - if err != nil { - util.HandleError(err, "Could not fetch secrets", "If you are using a service token to fetch secrets, please ensure it is valid") - } - - log.Debug().Msgf("injecting the following environment variables into shell: %v", injectableEnvironment.Variables) - - if watchMode { - executeCommandWithWatchMode(command, args, watchModeInterval, request, projectConfigDir, secretOverriding, token) - } else { - if cmd.Flags().Changed("command") { - command := cmd.Flag("command").Value.String() - err = executeMultipleCommandWithEnvs(command, injectableEnvironment.SecretsCount, injectableEnvironment.Variables) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - } else { - err = executeSingleCommandWithEnvs(args, injectableEnvironment.SecretsCount, injectableEnvironment.Variables) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - } - } - - }, -} - -func filterReservedEnvVars(env map[string]models.SingleEnvironmentVariable) { - var ( - reservedEnvVars = []string{ - "HOME", "PATH", "PS1", "PS2", - "PWD", "EDITOR", "XAUTHORITY", "USER", - "TERM", "TERMINFO", "SHELL", "MAIL", - } - - reservedEnvVarPrefixes = []string{ - "XDG_", - "LC_", - } - ) - - for _, reservedEnvName := range reservedEnvVars { - if _, ok := env[reservedEnvName]; ok { - delete(env, reservedEnvName) - util.PrintWarning(fmt.Sprintf("Infisical secret named [%v] has been removed because it is a reserved secret name", reservedEnvName)) - } - } - - for _, reservedEnvPrefix := range reservedEnvVarPrefixes { - for envName := range env { - if strings.HasPrefix(envName, reservedEnvPrefix) { - delete(env, envName) - util.PrintWarning(fmt.Sprintf("Infisical secret named [%v] has been removed because it contains a reserved prefix", envName)) - } - } - } -} - -func init() { - rootCmd.AddCommand(runCmd) - runCmd.Flags().String("token", "", "fetch secrets using service token or machine identity access token") - runCmd.Flags().String("projectId", "", "manually set the project ID to fetch secrets from when using machine identity based auth") - runCmd.Flags().StringP("env", "e", "dev", "set the environment (dev, prod, etc.) from which your secrets should be pulled from") - runCmd.Flags().Bool("expand", true, "parse shell parameter expansions in your secrets") - runCmd.Flags().Bool("include-imports", true, "import linked secrets ") - runCmd.Flags().Bool("recursive", false, "fetch secrets from all sub-folders") - runCmd.Flags().Bool("secret-overriding", true, "prioritizes personal secrets, if any, with the same name over shared secrets") - runCmd.Flags().Bool("watch", false, "enable reload of application when secrets change") - runCmd.Flags().Int("watch-interval", 10, "interval in seconds to check for secret changes") - runCmd.Flags().StringP("command", "c", "", "chained commands to execute (e.g. \"npm install && npm run dev; echo ...\")") - runCmd.Flags().StringP("tags", "t", "", "filter secrets by tag slugs ") - runCmd.Flags().String("path", "/", "get secrets within a folder path") - runCmd.Flags().String("project-config-dir", "", "explicitly set the directory where the .infisical.json resides") -} - -// Will execute a single command and pass in the given secrets into the process -func executeSingleCommandWithEnvs(args []string, secretsCount int, env []string) error { - command := args[0] - argsForCommand := args[1:] - - log.Info().Msgf(color.GreenString("Injecting %v Infisical secrets into your application process", secretsCount)) - - cmd := exec.Command(command, argsForCommand...) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - cmd.Env = env - - return execBasicCmd(cmd) -} - -func executeMultipleCommandWithEnvs(fullCommand string, secretsCount int, env []string) error { - shell := [2]string{"sh", "-c"} - if runtime.GOOS == "windows" { - shell = [2]string{"cmd", "/C"} - } else { - currentShell := os.Getenv("SHELL") - if currentShell != "" { - shell[0] = currentShell - } - } - - cmd := exec.Command(shell[0], shell[1], fullCommand) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - cmd.Env = env - - log.Info().Msgf(color.GreenString("Injecting %v Infisical secrets into your application process", secretsCount)) - log.Debug().Msgf("executing command: %s %s %s \n", shell[0], shell[1], fullCommand) - - return execBasicCmd(cmd) -} - -func execBasicCmd(cmd *exec.Cmd) error { - sigChannel := make(chan os.Signal, 1) - signal.Notify(sigChannel) - - if err := cmd.Start(); err != nil { - return err - } - - go func() { - for { - sig := <-sigChannel - _ = cmd.Process.Signal(sig) // process all sigs - } - }() - - if err := cmd.Wait(); err != nil { - _ = cmd.Process.Signal(os.Kill) - return fmt.Errorf("failed to wait for command termination: %v", err) - } - - waitStatus := cmd.ProcessState.Sys().(syscall.WaitStatus) - os.Exit(waitStatus.ExitStatus()) - return nil -} - -func waitForExitCommand(cmd *exec.Cmd) (int, error) { - if err := cmd.Wait(); err != nil { - // ignore errors - cmd.Process.Signal(os.Kill) // #nosec G104 - - if exitError, ok := err.(*exec.ExitError); ok { - return exitError.ExitCode(), exitError - } - - return 2, err - } - - waitStatus, ok := cmd.ProcessState.Sys().(syscall.WaitStatus) - if !ok { - return 2, fmt.Errorf("unexpected ProcessState type, expected syscall.WaitStatus, got %T", waitStatus) - } - return waitStatus.ExitStatus(), nil -} - -func executeCommandWithWatchMode(commandFlag string, args []string, watchModeInterval int, request models.GetAllSecretsParameters, projectConfigDir string, secretOverriding bool, token *models.TokenDetails) { - - var cmd *exec.Cmd - var err error - var lastSecretsFetch time.Time - var lastUpdateEvent time.Time - var watchMutex sync.Mutex - var processMutex sync.Mutex - var beingTerminated = false - var currentETag string - - if err != nil { - util.HandleError(err, "Failed to fetch secrets") - } - - runCommandWithWatcher := func(environmentVariables models.InjectableEnvironmentResult) { - currentETag = environmentVariables.ETag - secretsFetchedAt := time.Now() - if secretsFetchedAt.After(lastSecretsFetch) { - lastSecretsFetch = secretsFetchedAt - } - - shouldRestartProcess := cmd != nil - // terminate the old process before starting a new one - if shouldRestartProcess { - log.Info().Msg(color.HiMagentaString("[HOT RELOAD] Environment changes detected. Reloading process...")) - beingTerminated = true - - log.Debug().Msgf(color.HiMagentaString("[HOT RELOAD] Sending SIGTERM to PID %d", cmd.Process.Pid)) - if e := cmd.Process.Signal(syscall.SIGTERM); e != nil { - log.Error().Err(e).Msg(color.HiMagentaString("[HOT RELOAD] Failed to send SIGTERM")) - } - // wait up to 10 sec for the process to exit - for i := 0; i < 10; i++ { - if !util.IsProcessRunning(cmd.Process) { - // process has been killed so we break out - break - } - if i == 5 { - log.Debug().Msg(color.HiMagentaString("[HOT RELOAD] Still waiting for process exit status")) - } - time.Sleep(time.Second) - } - - // SIGTERM may not work on Windows so we try SIGKILL - if util.IsProcessRunning(cmd.Process) { - log.Debug().Msg(color.HiMagentaString("[HOT RELOAD] Process still hasn't fully exited, attempting SIGKILL")) - if e := cmd.Process.Kill(); e != nil { - log.Error().Err(e).Msg(color.HiMagentaString("[HOT RELOAD] Failed to send SIGKILL")) - } - } - - cmd = nil - } else { - // If `cmd` is nil, we know this is the first time we are starting the process - log.Info().Msg(color.HiMagentaString("[HOT RELOAD] Watching for secret changes...")) - } - - processMutex.Lock() - - if lastUpdateEvent.After(secretsFetchedAt) { - processMutex.Unlock() - return - } - - beingTerminated = false - watcherWaitGroup.Add(1) - - // start the process - log.Info().Msgf(color.GreenString("Injecting %v Infisical secrets into your application process", environmentVariables.SecretsCount)) - - cmd, err = util.RunCommand(commandFlag, args, environmentVariables.Variables, false) - if err != nil { - defer watcherWaitGroup.Done() - util.HandleError(err) - } - - go func() { - defer processMutex.Unlock() - defer watcherWaitGroup.Done() - - exitCode, err := waitForExitCommand(cmd) - - // ignore errors if we are being terminated - if !beingTerminated { - if err != nil { - if strings.HasPrefix(err.Error(), "exec") || strings.HasPrefix(err.Error(), "fork/exec") { - log.Error().Err(err).Msg("Failed to execute command") - } - if err.Error() != ErrManualSignalInterrupt.Error() { - log.Error().Err(err).Msg("Process exited with error") - } - } - - os.Exit(exitCode) - } - }() - } - - recheckSecretsChannel := make(chan bool, 1) - recheckSecretsChannel <- true - - // a simple goroutine that triggers the recheckSecretsChan every watch interval (defaults to 10 seconds) - go func() { - for { - time.Sleep(time.Duration(watchModeInterval) * time.Second) - recheckSecretsChannel <- true - } - }() - - for { - <-recheckSecretsChannel - func() { - watchMutex.Lock() - defer watchMutex.Unlock() - - newEnvironmentVariables, err := fetchAndFormatSecretsForShell(request, projectConfigDir, secretOverriding, token) - if err != nil { - log.Error().Err(err).Msg("[HOT RELOAD] Failed to fetch secrets") - return - } - - if newEnvironmentVariables.ETag != currentETag { - runCommandWithWatcher(newEnvironmentVariables) - } else { - log.Debug().Msg("[HOT RELOAD] No changes detected in secrets, not reloading process") - } - - }() - } -} - -func fetchAndFormatSecretsForShell(request models.GetAllSecretsParameters, projectConfigDir string, secretOverriding bool, token *models.TokenDetails) (models.InjectableEnvironmentResult, error) { - - if token != nil && token.Type == util.SERVICE_TOKEN_IDENTIFIER { - request.InfisicalToken = token.Token - } else if token != nil && token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER { - request.UniversalAuthAccessToken = token.Token - } - - secrets, err := util.GetAllEnvironmentVariables(request, projectConfigDir) - - if err != nil { - return models.InjectableEnvironmentResult{}, err - } - - if secretOverriding { - secrets = util.OverrideSecrets(secrets, util.SECRET_TYPE_PERSONAL) - } else { - secrets = util.OverrideSecrets(secrets, util.SECRET_TYPE_SHARED) - } - - secretsByKey := getSecretsByKeys(secrets) - environmentVariables := make(map[string]string) - - // add all existing environment vars - for _, s := range os.Environ() { - kv := strings.SplitN(s, "=", 2) - key := kv[0] - value := kv[1] - environmentVariables[key] = value - } - - // check to see if there are any reserved key words in secrets to inject - filterReservedEnvVars(secretsByKey) - - // now add infisical secrets - for k, v := range secretsByKey { - environmentVariables[k] = v.Value - } - - env := make([]string, 0, len(environmentVariables)) - for key, value := range environmentVariables { - env = append(env, key+"="+value) - } - - return models.InjectableEnvironmentResult{ - Variables: env, - ETag: util.GenerateETagFromSecrets(secrets), - SecretsCount: len(secretsByKey), - }, nil -} diff --git a/cli/packages/cmd/scan.go b/cli/packages/cmd/scan.go deleted file mode 100644 index 4a721d2c5..000000000 --- a/cli/packages/cmd/scan.go +++ /dev/null @@ -1,636 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Zachary Rice - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package cmd - -import ( - _ "embed" - "fmt" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "strings" - "time" - - "github.com/Infisical/infisical-merge/detect" - "github.com/Infisical/infisical-merge/detect/cmd/scm" - "github.com/Infisical/infisical-merge/detect/config" - "github.com/Infisical/infisical-merge/detect/logging" - "github.com/Infisical/infisical-merge/detect/report" - "github.com/Infisical/infisical-merge/detect/sources" - "github.com/Infisical/infisical-merge/packages/util" - "github.com/manifoldco/promptui" - "github.com/posthog/posthog-go" - "github.com/rs/zerolog/log" - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -const configDescription = `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 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") - scanCmd.Flags().Bool("no-git", false, "treat git repo as a regular directory and scan those files, --log-opts has no effect on the scan when --no-git is set") - scanCmd.Flags().Bool("pipe", false, "scan input from stdin, ex: `cat some_file | infisical scan --pipe`") - scanCmd.Flags().Bool("follow-symlinks", false, "scan files that are symlinks to other files") - - // global scan flags - scanCmd.PersistentFlags().StringP("config", "c", "", configDescription) - scanCmd.PersistentFlags().Int("exit-code", 1, "exit code when leaks have been encountered") - scanCmd.PersistentFlags().StringP("source", "s", ".", "path to source") - scanCmd.PersistentFlags().StringP("report-path", "r", "", "report file") - scanCmd.PersistentFlags().StringP("report-format", "f", "json", "output format (json, csv, sarif)") - scanCmd.PersistentFlags().StringP("baseline-path", "b", "", "path to baseline with issues that can be ignored") - scanCmd.PersistentFlags().BoolP("verbose", "v", false, "show verbose output from scan (which file, where in the file, what secret)") - scanCmd.PersistentFlags().BoolP("no-color", "", false, "turn off color for verbose output") - scanCmd.PersistentFlags().Int("max-target-megabytes", 0, "files larger than this will be skipped") - scanCmd.PersistentFlags().Bool("redact", false, "redact secrets from logs and stdout") - - // scan git changes command flags - scanGitChangesCmd.Flags().Bool("staged", false, "detect secrets in a --staged state") - scanGitChangesCmd.Flags().String("log-opts", "", "git log options") - - // find config source - err := viper.BindPFlag("config", scanCmd.PersistentFlags().Lookup("config")) - if err != nil { - log.Fatal().Msgf("err binding config %s", err.Error()) - } - - // 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) { - cfgPath, err := cmd.Flags().GetString("config") - if err != nil { - log.Fatal().Msg(err.Error()) - } - - if cfgPath != "" { - viper.SetConfigFile(cfgPath) - log.Debug().Msgf("using scan config %s from `--config`", cfgPath) - } else if os.Getenv(config.DefaultScanConfigEnvName) != "" { - envPath := os.Getenv(config.DefaultScanConfigEnvName) - viper.SetConfigFile(envPath) - log.Debug().Msgf("using scan config from %s env var: %s", config.DefaultScanConfigEnvName, envPath) - } else { - source, err := cmd.Flags().GetString("source") - if err != nil { - log.Fatal().Msg(err.Error()) - } - fileInfo, err := os.Stat(source) - if err != nil { - log.Fatal().Msg(err.Error()) - } - - if !fileInfo.IsDir() { - log.Debug().Msgf("unable to load scan config from %s since --source=%s is a file, using default config", - filepath.Join(source, config.DefaultScanConfigFileName), source) - viper.SetConfigType("toml") - if err = viper.ReadConfig(strings.NewReader(config.DefaultConfig)); err != nil { - log.Fatal().Msgf("err reading toml %s", err.Error()) - } - return - } - - if _, err := os.Stat(filepath.Join(source, config.DefaultScanConfigFileName)); os.IsNotExist(err) { - log.Debug().Msgf("no scan config found in path %s, using default scan config", filepath.Join(source, config.DefaultScanConfigFileName)) - viper.SetConfigType("toml") - if err = viper.ReadConfig(strings.NewReader(config.DefaultConfig)); err != nil { - log.Fatal().Msgf("err reading default scan config toml %s", err.Error()) - } - return - } else { - log.Debug().Msgf("using existing scan config %s from `(--source)/%s`", filepath.Join(source, config.DefaultScanConfigFileName), config.DefaultScanConfigFileName) - } - - viper.AddConfigPath(source) - viper.SetConfigName(config.DefaultScanConfigFileName) - viper.SetConfigType("toml") - } - if err := viper.ReadInConfig(); err != nil { - log.Fatal().Msgf("unable to load scan config, err: %s", err) - } -} - -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", - Run: func(cmd *cobra.Command, args []string) { - initScanConfig(cmd) - - var ( - vc config.ViperConfig - findings []report.Finding - err error - ) - - // Load config - if err = viper.Unmarshal(&vc); err != nil { - log.Fatal().Err(err).Msg("Failed to load config") - } - cfg, err := vc.Translate() - if err != nil { - log.Fatal().Err(err).Msg("Failed to load config") - } - cfg.Path, _ = cmd.Flags().GetString("config") - - // start timer - start := time.Now() - - // Setup detector - detector := detect.NewDetector(cfg) - detector.Config.Path, err = cmd.Flags().GetString("config") - if err != nil { - log.Fatal().Err(err).Msg("") - } - source, err := cmd.Flags().GetString("source") - if err != nil { - log.Fatal().Err(err).Msg("") - } - // if config path is not set, then use the {source}/.infisical-scan.toml path. - // note that there may not be a `{source}/.infisical-scan.toml` file, this is ok. - if detector.Config.Path == "" { - detector.Config.Path = filepath.Join(source, config.DefaultScanConfigFileName) - } - // set verbose flag - if detector.Verbose, err = cmd.Flags().GetBool("verbose"); err != nil { - log.Fatal().Err(err).Msg("") - } - // set redact flag - - redactFlag, err := cmd.Flags().GetBool("redact") - if err != nil { - log.Fatal().Err(err).Msg("") - } - if redactFlag { - detector.Redact = 100 - } else { - detector.Redact = 0 - } - - if detector.MaxTargetMegaBytes, err = cmd.Flags().GetInt("max-target-megabytes"); err != nil { - log.Fatal().Err(err).Msg("") - } - // set color flag - if detector.NoColor, err = cmd.Flags().GetBool("no-color"); err != nil { - log.Fatal().Err(err).Msg("") - } - - if fileExists(filepath.Join(source, config.DefaultInfisicalIgnoreFineName)) { - if err = detector.AddGitleaksIgnore(filepath.Join(source, config.DefaultInfisicalIgnoreFineName)); err != nil { - log.Fatal().Err(err).Msg("could not call AddInfisicalIgnore") - } - } - - // ignore findings from the baseline (an existing report in json format generated earlier) - baselinePath, _ := cmd.Flags().GetString("baseline-path") - if baselinePath != "" { - err = detector.AddBaseline(baselinePath, source) - if err != nil { - log.Error().Msgf("Could not load baseline. The path must point to report generated by `infisical scan` using the default format: %s", err) - } - } - - // set follow symlinks flag - if detector.FollowSymlinks, err = cmd.Flags().GetBool("follow-symlinks"); err != nil { - log.Fatal().Err(err).Msg("") - } - - // set exit code - exitCode, err := cmd.Flags().GetInt("exit-code") - if err != nil { - log.Fatal().Err(err).Msg("could not get exit code") - } - - // determine what type of scan: - // - git: scan the history of the repo - // - no-git: scan files by treating the repo as a plain directory - noGit, err := cmd.Flags().GetBool("no-git") - if err != nil { - log.Fatal().Err(err).Msg("could not call GetBool() for no-git") - } - fromPipe, err := cmd.Flags().GetBool("pipe") - if err != nil { - log.Fatal().Err(err) - } - - log.Info().Msgf("scanning for exposed secrets...") - - // start the detector scan - if noGit { - paths, err := sources.DirectoryTargets( - source, - detector.Sema, - detector.FollowSymlinks, - detector.Config.Allowlists, - ) - if err != nil { - logging.Fatal().Err(err).Send() - } - - if findings, err = detector.DetectFiles(paths); err != nil { - // don't exit on error, just log it - logging.Error().Err(err).Msg("failed scan directory") - } - } else if fromPipe { - if findings, err = detector.DetectReader(os.Stdin, 10); err != nil { - // log fatal to exit, no need to continue since a report - // will not be generated when scanning from a pipe...for now - logging.Fatal().Err(err).Msg("failed scan input from stdin") - } - } else { - var ( - gitCmd *sources.GitCmd - scmPlatform scm.Platform - remote *detect.RemoteInfo - ) - - var logOpts string - logOpts, err = cmd.Flags().GetString("log-opts") - - if gitCmd, err = sources.NewGitLogCmd(source, logOpts); err != nil { - logging.Fatal().Err(err).Msg("could not create Git cmd") - } - scmPlatform = scm.UnknownPlatform - remote = detect.NewRemoteInfo(scmPlatform, source) - - if findings, err = detector.DetectGit(gitCmd, remote); err != nil { - // don't exit on error, just log it - logging.Error().Err(err).Msg("failed to scan Git repository") - } - } - // log info about the scan - if err == nil { - log.Info().Msgf("scan completed in %s", FormatDuration(time.Since(start))) - if len(findings) != 0 { - log.Warn().Msgf("leaks found: %d", len(findings)) - } else { - log.Info().Msg("no leaks found") - } - } else { - log.Warn().Msgf("partial scan completed in %s", FormatDuration(time.Since(start))) - if len(findings) != 0 { - log.Warn().Msgf("%d leaks found in partial scan", len(findings)) - } else { - log.Warn().Msg("no leaks found in partial scan") - } - } - - 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") - if reportPath != "" { - reportFindings(findings, reportPath, ext, &cfg) - } - - if err != nil { - os.Exit(1) - } - - if len(findings) != 0 { - os.Exit(exitCode) - } - }, -} - -var scanGitChangesCmd = &cobra.Command{ - Use: "git-changes", - Short: "Scan for secrets in uncommitted changes in a git repo", - Run: func(cmd *cobra.Command, args []string) { - initScanConfig(cmd) - - var vc config.ViperConfig - - if err := viper.Unmarshal(&vc); err != nil { - log.Fatal().Err(err).Msg("Failed to load config") - } - cfg, err := vc.Translate() - if err != nil { - log.Fatal().Err(err).Msg("Failed to load config") - } - - cfg.Path, _ = cmd.Flags().GetString("config") - exitCode, _ := cmd.Flags().GetInt("exit-code") - staged, _ := cmd.Flags().GetBool("staged") - - // Setup detector - detector := detect.NewDetector(cfg) - detector.Config.Path, err = cmd.Flags().GetString("config") - if err != nil { - log.Fatal().Err(err).Msg("") - } - source, err := cmd.Flags().GetString("source") - if err != nil { - log.Fatal().Err(err).Msg("") - } - // if config path is not set, then use the {source}/.infisical-scan.toml path. - // note that there may not be a `{source}/.infisical-scan.toml` file, this is ok. - if detector.Config.Path == "" { - detector.Config.Path = filepath.Join(source, config.DefaultScanConfigFileName) - } - // set verbose flag - if detector.Verbose, err = cmd.Flags().GetBool("verbose"); err != nil { - log.Fatal().Err(err).Msg("") - } - // set redact flag - - redactFlag, err := cmd.Flags().GetBool("redact") - if err != nil { - log.Fatal().Err(err).Msg("") - } - if redactFlag { - detector.Redact = 100 - } else { - detector.Redact = 0 - } - - if detector.MaxTargetMegaBytes, err = cmd.Flags().GetInt("max-target-megabytes"); err != nil { - log.Fatal().Err(err).Msg("") - } - // set color flag - if detector.NoColor, err = cmd.Flags().GetBool("no-color"); err != nil { - log.Fatal().Err(err).Msg("") - } - - if fileExists(filepath.Join(source, config.DefaultInfisicalIgnoreFineName)) { - if err = detector.AddGitleaksIgnore(filepath.Join(source, config.DefaultInfisicalIgnoreFineName)); err != nil { - log.Fatal().Err(err).Msg("could not call AddInfisicalIgnore") - } - } - - // start git scan - var ( - findings []report.Finding - - gitCmd *sources.GitCmd - remote *detect.RemoteInfo - ) - - if gitCmd, err = sources.NewGitDiffCmd(source, staged); err != nil { - logging.Fatal().Err(err).Msg("could not create Git diff cmd") - } - remote = &detect.RemoteInfo{Platform: scm.NoPlatform} - - if findings, err = detector.DetectGit(gitCmd, remote); err != nil { - // don't exit on error, just log it - logging.Error().Err(err).Msg("failed to scan Git repository") - } - - 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 != "" { - reportFindings(findings, reportPath, ext, &cfg) - } - if len(findings) != 0 { - os.Exit(exitCode) - } - }, -} - -func reportFindings(findings []report.Finding, reportPath string, ext string, cfg *config.Config) { - - var reporter report.Reporter - - switch ext { - case "csv": - reporter = &report.CsvReporter{} - case "json": - reporter = &report.JsonReporter{} - case "junit": - reporter = &report.JunitReporter{} - case "sarif": - reporter = &report.SarifReporter{ - OrderedRules: cfg.GetOrderedRules(), - } - default: - logging.Fatal().Msgf("unknown report format %s", ext) - } - - file, err := os.Create(reportPath) - if err != nil { - log.Fatal().Err(err).Msg("could not create file") - } - - if err := reporter.Write(file, findings); err != nil { - log.Fatal().Err(err).Msg("could not write") - } - -} - -func fileExists(fileName string) bool { - // check for a .infisicalignore file - info, err := os.Stat(fileName) - if err != nil && !os.IsNotExist(err) { - return false - } - - if info != nil && err == nil { - if !info.IsDir() { - return true - } - } - return false -} - -func FormatDuration(d time.Duration) string { - scale := 100 * time.Second - // look for the max scale that is smaller than d - for scale > d { - scale = scale / 10 - } - 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 -} diff --git a/cli/packages/cmd/secrets.go b/cli/packages/cmd/secrets.go deleted file mode 100644 index 930a27a56..000000000 --- a/cli/packages/cmd/secrets.go +++ /dev/null @@ -1,782 +0,0 @@ -/* -Copyright (c) 2023 Infisical Inc. -*/ -package cmd - -import ( - "fmt" - "os" - "regexp" - "sort" - "strings" - - "github.com/Infisical/infisical-merge/packages/api" - "github.com/Infisical/infisical-merge/packages/models" - "github.com/Infisical/infisical-merge/packages/util" - "github.com/Infisical/infisical-merge/packages/visualize" - "github.com/posthog/posthog-go" - "github.com/spf13/cobra" -) - -var secretsCmd = &cobra.Command{ - Example: `infisical secrets`, - Short: "Used to create, read update and delete secrets", - Use: "secrets", - DisableFlagsInUseLine: true, - Args: cobra.NoArgs, - Run: func(cmd *cobra.Command, args []string) { - environmentName, _ := cmd.Flags().GetString("env") - if !cmd.Flags().Changed("env") { - environmentFromWorkspace := util.GetEnvFromWorkspaceFile() - if environmentFromWorkspace != "" { - environmentName = environmentFromWorkspace - } - } - - token, err := util.GetInfisicalToken(cmd) - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - projectId, err := cmd.Flags().GetString("projectId") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - secretsPath, err := cmd.Flags().GetString("path") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - shouldExpandSecrets, err := cmd.Flags().GetBool("expand") - if err != nil { - util.HandleError(err) - } - - includeImports, err := cmd.Flags().GetBool("include-imports") - if err != nil { - util.HandleError(err) - } - - recursive, err := cmd.Flags().GetBool("recursive") - if err != nil { - util.HandleError(err) - } - - tagSlugs, err := cmd.Flags().GetString("tags") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - secretOverriding, err := cmd.Flags().GetBool("secret-overriding") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - plainOutput, err := cmd.Flags().GetBool("plain") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - request := models.GetAllSecretsParameters{ - Environment: environmentName, - WorkspaceId: projectId, - TagSlugs: tagSlugs, - SecretsPath: secretsPath, - IncludeImport: includeImports, - Recursive: recursive, - ExpandSecretReferences: shouldExpandSecrets, - } - - if token != nil && token.Type == util.SERVICE_TOKEN_IDENTIFIER { - request.InfisicalToken = token.Token - } else if token != nil && token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER { - request.UniversalAuthAccessToken = token.Token - } - - secrets, err := util.GetAllEnvironmentVariables(request, "") - if err != nil { - util.HandleError(err) - } - - if secretOverriding { - secrets = util.OverrideSecrets(secrets, util.SECRET_TYPE_PERSONAL) - } else { - secrets = util.OverrideSecrets(secrets, util.SECRET_TYPE_SHARED) - } - - // Sort the secrets by key so we can create a consistent output - secrets = util.SortSecretsByKeys(secrets) - - if plainOutput { - for _, secret := range secrets { - fmt.Println(fmt.Sprintf("%s=%s", secret.Key, secret.Value)) - } - } else { - visualize.PrintAllSecretDetails(secrets) - } - - Telemetry.CaptureEvent("cli-command:secrets", posthog.NewProperties().Set("secretCount", len(secrets)).Set("version", util.CLI_VERSION)) - }, -} - -var secretsGetCmd = &cobra.Command{ - Example: `secrets get ..."`, - Short: "Used to retrieve secrets by name", - Use: "get [secrets]", - DisableFlagsInUseLine: true, - Args: cobra.MinimumNArgs(1), - Run: getSecretsByNames, -} - -var secretsGenerateExampleEnvCmd = &cobra.Command{ - Example: `secrets generate-example-env > .example-env`, - Short: "Used to generate a example .env file", - Use: "generate-example-env", - DisableFlagsInUseLine: true, - Args: cobra.NoArgs, - Run: generateExampleEnv, -} - -var secretsSetCmd = &cobra.Command{ - Example: `secrets set ..."`, - Short: "Used set secrets", - Use: "set [secrets]", - DisableFlagsInUseLine: true, - Args: func(cmd *cobra.Command, args []string) error { - if cmd.Flags().Changed("file") { - if len(args) > 0 { - return fmt.Errorf("secrets cannot be provided as command-line arguments when the --file option is used. Please choose either file-based or argument-based secret input") - } - return nil - } - return cobra.MinimumNArgs(1)(cmd, args) - }, - Run: func(cmd *cobra.Command, args []string) { - token, err := util.GetInfisicalToken(cmd) - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - environmentName, _ := cmd.Flags().GetString("env") - if !cmd.Flags().Changed("env") { - environmentFromWorkspace := util.GetEnvFromWorkspaceFile() - if environmentFromWorkspace != "" { - environmentName = environmentFromWorkspace - } - } - - projectId, err := cmd.Flags().GetString("projectId") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - if token == nil && projectId == "" { - _, err := util.GetWorkSpaceFromFile() - if err != nil { - util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag") - } - } - - secretsPath, err := cmd.Flags().GetString("path") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - secretType, err := cmd.Flags().GetString("type") - if err != nil || (secretType != util.SECRET_TYPE_SHARED && secretType != util.SECRET_TYPE_PERSONAL) { - util.HandleError(err, "Unable to parse secret type") - } - - processedArgs := []string{} - for _, arg := range args { - splitKeyValue := strings.SplitN(arg, "=", 2) - if len(splitKeyValue) != 2 { - util.HandleError(fmt.Errorf("invalid argument format: %s. Expected format: key=value or key=@filepath", arg), "") - } - - key := splitKeyValue[0] - value := splitKeyValue[1] - - if strings.HasPrefix(value, "\\@") { - value = "@" + value[2:] - } else if strings.HasPrefix(value, "@") { - filePath := strings.TrimPrefix(value, "@") - content, err := os.ReadFile(filePath) - if err != nil { - util.HandleError(err, fmt.Sprintf("Unable to read file %s", filePath)) - } - value = string(content) - } - - processedArgs = append(processedArgs, fmt.Sprintf("%s=%s", key, value)) - } - - file, err := cmd.Flags().GetString("file") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - var secretOperations []models.SecretSetOperation - if token != nil && (token.Type == util.SERVICE_TOKEN_IDENTIFIER || token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER) { - if projectId == "" { - util.PrintErrorMessageAndExit("When using service tokens or machine identities, you must set the --projectId flag") - } - - secretOperations, err = util.SetRawSecrets(args, secretType, environmentName, secretsPath, projectId, token, file) - } else { - if projectId == "" { - workspaceFile, err := util.GetWorkSpaceFromFile() - if err != nil { - util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag") - } - - projectId = workspaceFile.WorkspaceId - } - - loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true) - if err != nil { - util.HandleError(err, "unable to authenticate [err=%v]") - } - - if loggedInUserDetails.LoginExpired { - loggedInUserDetails = util.EstablishUserLoginSession() - } - - secretOperations, err = util.SetRawSecrets(processedArgs, secretType, environmentName, secretsPath, projectId, &models.TokenDetails{ - Type: "", - Token: loggedInUserDetails.UserCredentials.JTWToken, - }, file) - } - - if err != nil { - util.HandleError(err, "Unable to set secrets") - } - - // Print secret operations - headers := [...]string{"SECRET NAME", "SECRET VALUE", "STATUS"} - rows := [][3]string{} - for _, secretOperation := range secretOperations { - rows = append(rows, [...]string{secretOperation.SecretKey, secretOperation.SecretValue, secretOperation.SecretOperation}) - } - - visualize.Table(headers, rows) - - Telemetry.CaptureEvent("cli-command:secrets set", posthog.NewProperties().Set("version", util.CLI_VERSION)) - }, -} - -var secretsDeleteCmd = &cobra.Command{ - Example: `secrets delete ..."`, - Short: "Used to delete secrets by name", - Use: "delete [secrets]", - DisableFlagsInUseLine: true, - Args: cobra.MinimumNArgs(1), - Run: func(cmd *cobra.Command, args []string) { - environmentName, _ := cmd.Flags().GetString("env") - if !cmd.Flags().Changed("env") { - environmentFromWorkspace := util.GetEnvFromWorkspaceFile() - if environmentFromWorkspace != "" { - environmentName = environmentFromWorkspace - } - } - - token, err := util.GetInfisicalToken(cmd) - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - projectId, err := cmd.Flags().GetString("projectId") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - secretsPath, err := cmd.Flags().GetString("path") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - secretType, err := cmd.Flags().GetString("type") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - httpClient, err := util.GetRestyClientWithCustomHeaders() - if err != nil { - util.HandleError(err, "Unable to get resty client with custom headers") - } - - httpClient.SetHeader("Accept", "application/json") - - if projectId == "" { - workspaceFile, err := util.GetWorkSpaceFromFile() - if err != nil { - util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag") - } - projectId = workspaceFile.WorkspaceId - } - - if token != nil && (token.Type == util.SERVICE_TOKEN_IDENTIFIER || token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER) { - httpClient.SetAuthToken(token.Token) - } else { - util.RequireLogin() - - loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true) - if err != nil { - util.HandleError(err, "Unable to authenticate") - } - - if loggedInUserDetails.LoginExpired { - loggedInUserDetails = util.EstablishUserLoginSession() - } - - httpClient.SetAuthToken(loggedInUserDetails.UserCredentials.JTWToken) - } - - for _, secretName := range args { - request := api.DeleteSecretV3Request{ - WorkspaceId: projectId, - Environment: environmentName, - SecretName: secretName, - Type: secretType, - SecretPath: secretsPath, - } - - err = api.CallDeleteSecretsRawV3(httpClient, request) - if err != nil { - util.HandleError(err, "Unable to complete your delete request") - } - } - - 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(args)).Set("version", util.CLI_VERSION)) - }, -} - -func getSecretsByNames(cmd *cobra.Command, args []string) { - environmentName, _ := cmd.Flags().GetString("env") - if !cmd.Flags().Changed("env") { - environmentFromWorkspace := util.GetEnvFromWorkspaceFile() - if environmentFromWorkspace != "" { - environmentName = environmentFromWorkspace - } - } - - token, err := util.GetInfisicalToken(cmd) - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - shouldExpand, err := cmd.Flags().GetBool("expand") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - tagSlugs, err := cmd.Flags().GetString("tags") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - projectId, err := cmd.Flags().GetString("projectId") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - secretsPath, err := cmd.Flags().GetString("path") - if err != nil { - util.HandleError(err, "Unable to parse path flag") - } - - recursive, err := cmd.Flags().GetBool("recursive") - if err != nil { - util.HandleError(err, "Unable to parse recursive flag") - } - - // deprecated, in favor of --plain - showOnlyValue, err := cmd.Flags().GetBool("raw-value") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - plainOutput, err := cmd.Flags().GetBool("plain") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - includeImports, err := cmd.Flags().GetBool("include-imports") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - secretOverriding, err := cmd.Flags().GetBool("secret-overriding") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - request := models.GetAllSecretsParameters{ - Environment: environmentName, - WorkspaceId: projectId, - TagSlugs: tagSlugs, - SecretsPath: secretsPath, - IncludeImport: includeImports, - Recursive: recursive, - ExpandSecretReferences: shouldExpand, - } - - if token != nil && token.Type == util.SERVICE_TOKEN_IDENTIFIER { - request.InfisicalToken = token.Token - } else if token != nil && token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER { - request.UniversalAuthAccessToken = token.Token - } - - secrets, err := util.GetAllEnvironmentVariables(request, "") - if err != nil { - util.HandleError(err, "To fetch all secrets") - } - - if secretOverriding { - secrets = util.OverrideSecrets(secrets, util.SECRET_TYPE_PERSONAL) - } else { - secrets = util.OverrideSecrets(secrets, util.SECRET_TYPE_SHARED) - } - - requestedSecrets := []models.SingleEnvironmentVariable{} - - secretsMap := getSecretsByKeys(secrets) - - for _, secretKeyFromArg := range args { - if value, ok := secretsMap[secretKeyFromArg]; ok { - requestedSecrets = append(requestedSecrets, value) - } else { - if !(plainOutput || showOnlyValue) { - requestedSecrets = append(requestedSecrets, models.SingleEnvironmentVariable{ - Key: secretKeyFromArg, - Type: "*not found*", - Value: "*not found*", - }) - } - } - } - - // showOnlyValue deprecated in favor of --plain, below only for backward compatibility - if plainOutput || showOnlyValue { - for _, secret := range requestedSecrets { - fmt.Println(secret.Value) - } - } else { - 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) { - environmentName, _ := cmd.Flags().GetString("env") - if !cmd.Flags().Changed("env") { - environmentFromWorkspace := util.GetEnvFromWorkspaceFile() - if environmentFromWorkspace != "" { - environmentName = environmentFromWorkspace - } - } - - secretsPath, err := cmd.Flags().GetString("path") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - token, err := util.GetInfisicalToken(cmd) - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - projectId, err := cmd.Flags().GetString("projectId") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - tagSlugs, err := cmd.Flags().GetString("tags") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - request := models.GetAllSecretsParameters{ - Environment: environmentName, - WorkspaceId: projectId, - TagSlugs: tagSlugs, - SecretsPath: secretsPath, - IncludeImport: true, - } - - if token != nil && token.Type == util.SERVICE_TOKEN_IDENTIFIER { - request.InfisicalToken = token.Token - } else if token != nil && token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER { - request.UniversalAuthAccessToken = token.Token - } - - secrets, err := util.GetAllEnvironmentVariables(request, "") - if err != nil { - util.HandleError(err, "To fetch all secrets") - } - - tagsHashToSecretKey := make(map[string]int) - slugsToFilerBy := make(map[string]int) - - for _, slug := range strings.Split(tagSlugs, ",") { - slugsToFilerBy[slug] = 1 - } - - type TagsAndSecrets struct { - Secrets []models.SingleEnvironmentVariable - Tags []struct { - ID string `json:"_id"` - Name string `json:"name"` - Slug string `json:"slug"` - Workspace string `json:"workspace"` - } - } - - // sort secrets by associated tags (most number of tags to least tags) - sort.Slice(secrets, func(i, j int) bool { - return len(secrets[i].Tags) > len(secrets[j].Tags) - }) - - for i, secret := range secrets { - filteredTag := []struct { - ID string "json:\"_id\"" - Name string "json:\"name\"" - Slug string "json:\"slug\"" - Workspace string "json:\"workspace\"" - }{} - - for _, secretTag := range secret.Tags { - _, exists := slugsToFilerBy[secretTag.Slug] - if !exists { - filteredTag = append(filteredTag, secretTag) - } - } - - secret.Tags = filteredTag - secrets[i] = secret - } - - for _, secret := range secrets { - listOfTagSlugs := []string{} - - for _, tag := range secret.Tags { - listOfTagSlugs = append(listOfTagSlugs, tag.Slug) - } - sort.Strings(listOfTagSlugs) - - tagsHash := util.GetHashFromStringList(listOfTagSlugs) - - tagsHashToSecretKey[tagsHash] += 1 - } - - finalTagHashToSecretKey := make(map[string]TagsAndSecrets) - - for _, secret := range secrets { - listOfTagSlugs := []string{} - for _, tag := range secret.Tags { - listOfTagSlugs = append(listOfTagSlugs, tag.Slug) - } - - // sort the slug so we get the same hash each time - sort.Strings(listOfTagSlugs) - - tagsHash := util.GetHashFromStringList(listOfTagSlugs) - occurrence, exists := tagsHashToSecretKey[tagsHash] - if exists && occurrence > 0 { - - value, exists2 := finalTagHashToSecretKey[tagsHash] - allSecretsForTags := append(value.Secrets, secret) - - // sort the the secrets by keys so that they can later be sorted by the first item in the secrets array - sort.Slice(allSecretsForTags, func(i, j int) bool { - return allSecretsForTags[i].Key < allSecretsForTags[j].Key - }) - - if exists2 { - finalTagHashToSecretKey[tagsHash] = TagsAndSecrets{ - Tags: secret.Tags, - Secrets: allSecretsForTags, - } - } else { - finalTagHashToSecretKey[tagsHash] = TagsAndSecrets{ - Tags: secret.Tags, - Secrets: []models.SingleEnvironmentVariable{secret}, - } - } - - tagsHashToSecretKey[tagsHash] -= 1 - } - } - - // sort the fianl result by secret key fo consistent print order - listOfsecretDetails := make([]TagsAndSecrets, 0, len(finalTagHashToSecretKey)) - for _, secretDetails := range finalTagHashToSecretKey { - listOfsecretDetails = append(listOfsecretDetails, secretDetails) - } - - // sort the order of the headings by the order of the secrets - sort.Slice(listOfsecretDetails, func(i, j int) bool { - return len(listOfsecretDetails[i].Tags) < len(listOfsecretDetails[j].Tags) - }) - - tableOfContents := []string{} - fullyGeneratedDocuments := []string{} - for _, secretDetails := range listOfsecretDetails { - listOfKeyValue := []string{} - - for _, secret := range secretDetails.Secrets { - re := regexp.MustCompile(`(?s)(.*)DEFAULT:(.*)`) - match := re.FindStringSubmatch(secret.Comment) - defaultValue := "" - comment := secret.Comment - - // Case: Only has default value - if len(match) == 2 { - defaultValue = strings.TrimSpace(match[1]) - } - - // Case: has a comment and a default value - if len(match) == 3 { - comment = match[1] - defaultValue = match[2] - } - - row := "" - if comment != "" { - comment = addHash(comment) - row = fmt.Sprintf("%s \n%s=%s", strings.TrimSpace(comment), strings.TrimSpace(secret.Key), strings.TrimSpace(defaultValue)) - } else { - row = fmt.Sprintf("%s=%s", strings.TrimSpace(secret.Key), strings.TrimSpace(defaultValue)) - } - - // each secret row to be added to the file - listOfKeyValue = append(listOfKeyValue, row) - } - - listOfTagNames := []string{} - for _, tag := range secretDetails.Tags { - listOfTagNames = append(listOfTagNames, tag.Name) - } - - heading := CenterString(strings.Join(listOfTagNames, " & "), 80) - - if len(listOfTagNames) == 0 { - fullyGeneratedDocuments = append(fullyGeneratedDocuments, fmt.Sprintf("\n%s \n", strings.Join(listOfKeyValue, "\n"))) - } else { - fullyGeneratedDocuments = append(fullyGeneratedDocuments, fmt.Sprintf("\n\n\n%s \n%s \n", heading, strings.Join(listOfKeyValue, "\n"))) - tableOfContents = append(tableOfContents, strings.ToUpper(strings.Join(listOfTagNames, " & "))) - } - } - - dashedList := []string{} - for _, item := range tableOfContents { - dashedList = append(dashedList, fmt.Sprintf("# - %s \n", item)) - } - if len(dashedList) > 0 { - fmt.Println(CenterString("TABLE OF CONTENTS", 80)) - 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 { - stars := strings.Repeat("*", numStars) - padding := (numStars - len(s)) / 2 - cenetredTextWithStar := stars[:padding] + " " + s + " " + stars[padding:] - - hashes := strings.Repeat("#", len(cenetredTextWithStar)+2) - return fmt.Sprintf("%s \n# %s \n%s", hashes, cenetredTextWithStar, hashes) -} - -func addHash(input string) string { - lines := strings.Split(input, "\n") - for i, line := range lines { - lines[i] = "# " + line - } - return strings.Join(lines, "\n") -} - -func getSecretsByKeys(secrets []models.SingleEnvironmentVariable) map[string]models.SingleEnvironmentVariable { - secretMapByName := make(map[string]models.SingleEnvironmentVariable, len(secrets)) - - for _, secret := range secrets { - secretMapByName[secret.Key] = secret - } - - return secretMapByName -} - -func init() { - secretsGenerateExampleEnvCmd.Flags().String("token", "", "Fetch secrets using service token or machine identity access token") - secretsGenerateExampleEnvCmd.Flags().String("projectId", "", "manually set the projectId when using machine identity based auth") - secretsGenerateExampleEnvCmd.Flags().String("path", "/", "Fetch secrets from within a folder path") - secretsCmd.AddCommand(secretsGenerateExampleEnvCmd) - - secretsGetCmd.Flags().String("token", "", "Fetch secrets using service token or machine identity access token") - secretsGetCmd.Flags().String("projectId", "", "manually set the project ID to fetch secrets from when using machine identity based auth") - secretsGetCmd.Flags().String("path", "/", "get secrets within a folder path") - secretsGetCmd.Flags().Bool("plain", false, "print values without formatting, one per line") - secretsGetCmd.Flags().Bool("raw-value", false, "deprecated. Returns only the value of secret, only works with one secret. Use --plain instead") - secretsGetCmd.Flags().Bool("include-imports", true, "Imported linked secrets ") - secretsGetCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets, and process your referenced secrets") - secretsGetCmd.Flags().Bool("recursive", false, "Fetch secrets from all sub-folders") - secretsGetCmd.Flags().Bool("secret-overriding", true, "Prioritizes personal secrets, if any, with the same name over shared secrets") - secretsCmd.AddCommand(secretsGetCmd) - secretsCmd.Flags().Bool("secret-overriding", true, "Prioritizes personal secrets, if any, with the same name over shared secrets") - secretsCmd.AddCommand(secretsSetCmd) - secretsSetCmd.Flags().String("token", "", "Fetch secrets using service token or machine identity access token") - secretsSetCmd.Flags().String("projectId", "", "manually set the project ID to for setting secrets when using machine identity based auth") - secretsSetCmd.Flags().String("path", "/", "set secrets within a folder path") - secretsSetCmd.Flags().String("type", util.SECRET_TYPE_SHARED, "the type of secret to create: personal or shared") - secretsSetCmd.Flags().String("file", "", "Load secrets from the specified file. File format: .env or YAML (comments: # or //). This option is mutually exclusive with command-line secrets arguments.") - - secretsDeleteCmd.Flags().String("type", "personal", "the type of secret to delete: personal or shared (default: personal)") - secretsDeleteCmd.Flags().String("token", "", "Fetch secrets using service token or machine identity access token") - secretsDeleteCmd.Flags().String("projectId", "", "manually set the projectId to delete secrets from when using machine identity based auth") - secretsDeleteCmd.Flags().String("path", "/", "get secrets within a folder path") - secretsCmd.AddCommand(secretsDeleteCmd) - - // *** Folders sub command *** - folderCmd.PersistentFlags().String("env", "dev", "Used to select the environment name on which actions should be taken on") - - // Add getCmd, createCmd and deleteCmd flags here - getCmd.Flags().StringP("path", "p", "/", "The path from where folders should be fetched from") - getCmd.Flags().String("token", "", "Fetch secrets using service token or machine identity access token") - getCmd.Flags().String("projectId", "", "manually set the projectId to fetch folders from when using machine identity based auth") - folderCmd.AddCommand(getCmd) - - // Add createCmd flags here - createCmd.Flags().StringP("path", "p", "/", "Path to where the folder should be created") - createCmd.Flags().StringP("name", "n", "", "Name of the folder to be created in selected `--path`") - createCmd.Flags().String("token", "", "Fetch secrets using service token or machine identity access token") - createCmd.Flags().String("projectId", "", "manually set the project ID for creating folders in when using machine identity based auth") - folderCmd.AddCommand(createCmd) - - // Add deleteCmd flags here - deleteCmd.Flags().StringP("path", "p", "/", "Path to the folder to be deleted") - deleteCmd.Flags().String("token", "", "Fetch secrets using service token or machine identity access token") - deleteCmd.Flags().String("projectId", "", "manually set the projectId to delete folders when using machine identity based auth") - deleteCmd.Flags().StringP("name", "n", "", "Name of the folder to be deleted within selected `--path`") - folderCmd.AddCommand(deleteCmd) - - secretsCmd.AddCommand(folderCmd) - - // ** End of folders sub command - - secretsCmd.Flags().String("token", "", "Fetch secrets using service token or machine identity access token") - secretsCmd.Flags().String("projectId", "", "manually set the projectId to fetch secrets when using machine identity based auth") - secretsCmd.PersistentFlags().String("env", "dev", "Used to select the environment name on which actions should be taken on") - secretsCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets, and process your referenced secrets") - secretsCmd.Flags().Bool("include-imports", true, "Imported linked secrets ") - secretsCmd.Flags().Bool("recursive", false, "Fetch secrets from all sub-folders") - secretsCmd.PersistentFlags().StringP("tags", "t", "", "filter secrets by tag slugs") - secretsCmd.Flags().String("path", "/", "get secrets within a folder path") - secretsCmd.Flags().Bool("plain", false, "print values without formatting, one per line") - rootCmd.AddCommand(secretsCmd) -} diff --git a/cli/packages/cmd/ssh.go b/cli/packages/cmd/ssh.go deleted file mode 100644 index 4315989bd..000000000 --- a/cli/packages/cmd/ssh.go +++ /dev/null @@ -1,1142 +0,0 @@ -/* -Copyright (c) 2023 Infisical Inc. -*/ -package cmd - -import ( - "context" - "fmt" - "net" - "os" - "os/exec" - "path/filepath" - "strings" - "time" - - "github.com/Infisical/infisical-merge/packages/api" - "github.com/Infisical/infisical-merge/packages/config" - "github.com/Infisical/infisical-merge/packages/util" - infisicalSdk "github.com/infisical/go-sdk" - infisicalSdkUtil "github.com/infisical/go-sdk/packages/util" - "github.com/manifoldco/promptui" - "github.com/spf13/cobra" - "golang.org/x/crypto/ssh" - "golang.org/x/crypto/ssh/agent" -) - -var sshCmd = &cobra.Command{ - Example: `infisical ssh`, - Short: "Used to issue SSH credentials", - Use: "ssh", - DisableFlagsInUseLine: true, - Args: cobra.NoArgs, -} - -var sshIssueCredentialsCmd = &cobra.Command{ - Example: `ssh issue-credentials`, - Short: "Used to issue SSH credentials against a certificate template", - Use: "issue-credentials", - DisableFlagsInUseLine: true, - Args: cobra.NoArgs, - Run: issueCredentials, -} - -var sshSignKeyCmd = &cobra.Command{ - Example: `ssh sign-key`, - Short: "Used to sign a SSH public key against a certificate template", - Use: "sign-key", - DisableFlagsInUseLine: true, - Args: cobra.NoArgs, - Run: signKey, -} - -var sshConnectCmd = &cobra.Command{ - Use: "connect", - Short: "Connect to an SSH host using issued credentials", - Run: sshConnect, -} - -var sshAddHostCmd = &cobra.Command{ - Use: "add-host", - Short: "Register a new SSH host with Infisical", - Run: sshAddHost, -} - -var algoToFileName = map[infisicalSdkUtil.CertKeyAlgorithm]string{ - infisicalSdkUtil.RSA2048: "id_rsa_2048", - infisicalSdkUtil.RSA4096: "id_rsa_4096", - infisicalSdkUtil.ECDSAP256: "id_ecdsa_p256", - infisicalSdkUtil.ECDSAP384: "id_ecdsa_p384", -} - -func isValidKeyAlgorithm(algo infisicalSdkUtil.CertKeyAlgorithm) bool { - _, exists := algoToFileName[algo] - return exists -} - -func isValidCertType(certType infisicalSdkUtil.SshCertType) bool { - switch certType { - case infisicalSdkUtil.UserCert, infisicalSdkUtil.HostCert: - return true - default: - return false - } -} - -func writeToFile(filePath string, content string, perm os.FileMode) error { - // Ensure the directory exists - dir := filepath.Dir(filePath) - if err := os.MkdirAll(dir, 0755); err != nil { - return fmt.Errorf("failed to create directory %s: %w", dir, err) - } - - // Write the content to the file - err := os.WriteFile(filePath, []byte(content), perm) - if err != nil { - return fmt.Errorf("failed to write to file %s: %w", filePath, err) - } - - return nil -} - -func addCredentialsToAgent(privateKeyContent, certContent string) error { - // Parse the private key - privateKey, err := ssh.ParseRawPrivateKey([]byte(privateKeyContent)) - if err != nil { - return fmt.Errorf("failed to parse private key: %w", err) - } - - // Parse the certificate - pubKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(certContent)) - if err != nil { - return fmt.Errorf("failed to parse certificate: %w", err) - } - - cert, ok := pubKey.(*ssh.Certificate) - if !ok { - return fmt.Errorf("parsed key is not a certificate") - } - // Calculate LifetimeSecs based on certificate's valid-to time - validUntil := time.Unix(int64(cert.ValidBefore), 0) - now := time.Now() - - // Handle ValidBefore as either a timestamp or an enumeration - // SSH certificates use ValidBefore as a timestamp unless set to 0 or ~0 - if cert.ValidBefore == ssh.CertTimeInfinity { - // If certificate never expires, set default lifetime to 1 year (can adjust as needed) - validUntil = now.Add(365 * 24 * time.Hour) - } - - // Calculate the duration until expiration - lifetime := validUntil.Sub(now) - if lifetime <= 0 { - return fmt.Errorf("certificate is already expired") - } - - // Convert duration to seconds - lifetimeSecs := uint32(lifetime.Seconds()) - - // Connect to the SSH agent - socket := os.Getenv("SSH_AUTH_SOCK") - if socket == "" { - return fmt.Errorf("SSH_AUTH_SOCK not set") - } - - conn, err := net.Dial("unix", socket) - if err != nil { - return fmt.Errorf("failed to connect to SSH agent: %w", err) - } - defer conn.Close() - - agentClient := agent.NewClient(conn) - - // Add the key with certificate to the agent - err = agentClient.Add(agent.AddedKey{ - PrivateKey: privateKey, - Certificate: cert, - Comment: "Added via Infisical CLI", - LifetimeSecs: lifetimeSecs, - }) - if err != nil { - return fmt.Errorf("failed to add key to agent: %w", err) - } - - return nil -} - -func issueCredentials(cmd *cobra.Command, args []string) { - - token, err := util.GetInfisicalToken(cmd) - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - var infisicalToken string - - if token != nil && (token.Type == util.SERVICE_TOKEN_IDENTIFIER || token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER) { - infisicalToken = token.Token - } else { - util.RequireLogin() - - loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true) - if err != nil { - util.HandleError(err, "Unable to authenticate") - } - - if loggedInUserDetails.LoginExpired { - loggedInUserDetails = util.EstablishUserLoginSession() - } - infisicalToken = loggedInUserDetails.UserCredentials.JTWToken - } - - certificateTemplateId, err := cmd.Flags().GetString("certificateTemplateId") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - if certificateTemplateId == "" { - util.PrintErrorMessageAndExit("You must set the --certificateTemplateId flag") - } - - principalsStr, err := cmd.Flags().GetString("principals") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - // Check if the input string is empty before splitting - if principalsStr == "" { - util.HandleError(fmt.Errorf("no principals provided"), "The 'principals' flag cannot be empty") - } - - // Convert the comma-delimited string into a slice of strings - principals := strings.Split(principalsStr, ",") - for i, principal := range principals { - principals[i] = strings.TrimSpace(principal) - } - - keyAlgorithm, err := cmd.Flags().GetString("keyAlgorithm") - if err != nil { - util.HandleError(err, "Unable to parse keyAlgorithm flag") - } - - if !isValidKeyAlgorithm(infisicalSdkUtil.CertKeyAlgorithm(keyAlgorithm)) { - util.HandleError(fmt.Errorf("invalid keyAlgorithm: %s", keyAlgorithm), - "Valid values: RSA_2048, RSA_4096, EC_prime256v1, EC_secp384r1") - } - - certType, err := cmd.Flags().GetString("certType") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - if !isValidCertType(infisicalSdkUtil.SshCertType(certType)) { - util.HandleError(fmt.Errorf("invalid certType: %s", certType), - "Valid values: user, host") - } - - ttl, err := cmd.Flags().GetString("ttl") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - keyId, err := cmd.Flags().GetString("keyId") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - outFilePath, err := cmd.Flags().GetString("outFilePath") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - addToAgent, err := cmd.Flags().GetBool("addToAgent") - if err != nil { - util.HandleError(err, "Unable to parse addToAgent flag") - } - - if outFilePath == "" && !addToAgent { - util.PrintErrorMessageAndExit("You must provide either --outFilePath or --addToAgent flag to use this command") - } - - var ( - outputDir string - privateKeyPath string - publicKeyPath string - signedKeyPath string - ) - - if outFilePath != "" { - // Expand ~ to home directory if present - if strings.HasPrefix(outFilePath, "~") { - homeDir, err := os.UserHomeDir() - if err != nil { - util.HandleError(err, "Failed to resolve home directory") - } - outFilePath = strings.Replace(outFilePath, "~", homeDir, 1) - } - - // Check if outFilePath ends with "-cert.pub" - if strings.HasSuffix(outFilePath, "-cert.pub") { - // Treat outFilePath as the signed key path - signedKeyPath = outFilePath - - // Derive the base name by removing "-cert.pub" - baseName := strings.TrimSuffix(filepath.Base(outFilePath), "-cert.pub") - - // Set the output directory - outputDir = filepath.Dir(outFilePath) - - // Define private and public key paths - privateKeyPath = filepath.Join(outputDir, baseName) - publicKeyPath = filepath.Join(outputDir, baseName+".pub") - } else { - // Treat outFilePath as a directory - outputDir = outFilePath - - // Check if the directory exists; if not, create it - info, err := os.Stat(outputDir) - if os.IsNotExist(err) { - err = os.MkdirAll(outputDir, 0755) - if err != nil { - util.HandleError(err, "Failed to create output directory") - } - } else if err != nil { - util.HandleError(err, "Failed to access output directory") - } else if !info.IsDir() { - util.PrintErrorMessageAndExit("The provided --outFilePath is not a directory") - } - } - } - - // Define file names based on key algorithm - fileName := algoToFileName[infisicalSdkUtil.CertKeyAlgorithm(keyAlgorithm)] - - // Define file paths - privateKeyPath = filepath.Join(outputDir, fileName) - publicKeyPath = filepath.Join(outputDir, fileName+".pub") - signedKeyPath = filepath.Join(outputDir, fileName+"-cert.pub") - - // If outFilePath ends with "-cert.pub", ensure the signedKeyPath is set - if strings.HasSuffix(outFilePath, "-cert.pub") { - // Ensure the signedKeyPath was set - if signedKeyPath == "" { - util.HandleError(fmt.Errorf("signedKeyPath is not set correctly"), "Internal error") - } - } else { - // Ensure all paths are set - if privateKeyPath == "" || publicKeyPath == "" || signedKeyPath == "" { - util.HandleError(fmt.Errorf("file paths are not set correctly"), "Internal error") - } - } - - customHeaders, err := util.GetInfisicalCustomHeadersMap() - if err != nil { - util.HandleError(err, "Unable to get custom headers") - } - - infisicalClient := infisicalSdk.NewInfisicalClient(context.Background(), infisicalSdk.Config{ - SiteUrl: config.INFISICAL_URL, - UserAgent: api.USER_AGENT, - AutoTokenRefresh: false, - CustomHeaders: customHeaders, - }) - infisicalClient.Auth().SetAccessToken(infisicalToken) - - creds, err := infisicalClient.Ssh().IssueCredentials(infisicalSdk.IssueSshCredsOptions{ - CertificateTemplateID: certificateTemplateId, - Principals: principals, - KeyAlgorithm: infisicalSdkUtil.CertKeyAlgorithm(keyAlgorithm), - CertType: infisicalSdkUtil.SshCertType(certType), - TTL: ttl, - KeyID: keyId, - }) - - if err != nil { - util.HandleError(err, "Failed to issue SSH credentials") - } - - if outFilePath != "" { - // If signedKeyPath wasn't set in the directory scenario, set it now - if signedKeyPath == "" { - fileName := algoToFileName[infisicalSdkUtil.CertKeyAlgorithm(keyAlgorithm)] - signedKeyPath = filepath.Join(outputDir, fileName+"-cert.pub") - } - - if privateKeyPath == "" { - privateKeyPath = filepath.Join(outputDir, algoToFileName[infisicalSdkUtil.CertKeyAlgorithm(keyAlgorithm)]) - } - err = writeToFile(privateKeyPath, creds.PrivateKey, 0600) - if err != nil { - util.HandleError(err, "Failed to write Private Key to file") - } - - if publicKeyPath == "" { - publicKeyPath = privateKeyPath + ".pub" - } - err = writeToFile(publicKeyPath, creds.PublicKey, 0644) - if err != nil { - util.HandleError(err, "Failed to write Public Key to file") - } - - err = writeToFile(signedKeyPath, creds.SignedKey, 0644) - if err != nil { - util.HandleError(err, "Failed to write Signed Key to file") - } - - fmt.Println("Successfully wrote SSH certificate to:", signedKeyPath) - } - - // Add SSH credentials to the SSH agent if needed - if addToAgent { - // Call the helper function to handle add-to-agent flow - err := addCredentialsToAgent(creds.PrivateKey, creds.SignedKey) - if err != nil { - util.HandleError(err, "Failed to add keys to SSH agent") - } else { - fmt.Println("The SSH key and certificate have been successfully added to your ssh-agent.") - } - } -} - -func signKey(cmd *cobra.Command, args []string) { - - token, err := util.GetInfisicalToken(cmd) - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - var infisicalToken string - - if token != nil && (token.Type == util.SERVICE_TOKEN_IDENTIFIER || token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER) { - infisicalToken = token.Token - } else { - util.RequireLogin() - - loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true) - if err != nil { - util.HandleError(err, "Unable to authenticate") - } - - if loggedInUserDetails.LoginExpired { - loggedInUserDetails = util.EstablishUserLoginSession() - } - infisicalToken = loggedInUserDetails.UserCredentials.JTWToken - } - - certificateTemplateId, err := cmd.Flags().GetString("certificateTemplateId") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - if certificateTemplateId == "" { - util.PrintErrorMessageAndExit("You must set the --certificateTemplateId flag") - } - - publicKey, err := cmd.Flags().GetString("publicKey") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - publicKeyFilePath, err := cmd.Flags().GetString("publicKeyFilePath") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - if publicKey == "" && publicKeyFilePath == "" { - util.HandleError(fmt.Errorf("either --publicKey or --publicKeyFilePath must be provided"), "Invalid input") - } - - if publicKey != "" && publicKeyFilePath != "" { - util.HandleError(fmt.Errorf("only one of --publicKey or --publicKeyFile can be provided"), "Invalid input") - } - - if publicKeyFilePath != "" { - if strings.HasPrefix(publicKeyFilePath, "~") { - // Expand the tilde (~) to the user's home directory - homeDir, err := os.UserHomeDir() - if err != nil { - util.HandleError(err, "Failed to resolve home directory") - } - publicKeyFilePath = strings.Replace(publicKeyFilePath, "~", homeDir, 1) - } - - // Ensure the file has a .pub extension - if !strings.HasSuffix(publicKeyFilePath, ".pub") { - util.HandleError(fmt.Errorf("public key file must have a .pub extension"), "Invalid input") - } - - content, err := os.ReadFile(publicKeyFilePath) - if err != nil { - util.HandleError(err, "Failed to read public key file") - } - - publicKey = strings.TrimSpace(string(content)) - } - - if strings.TrimSpace(publicKey) == "" { - util.HandleError(fmt.Errorf("Public key is empty"), "Invalid input") - } - - principalsStr, err := cmd.Flags().GetString("principals") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - // Check if the input string is empty before splitting - if principalsStr == "" { - util.HandleError(fmt.Errorf("no principals provided"), "The 'principals' flag cannot be empty") - } - - // Convert the comma-delimited string into a slice of strings - principals := strings.Split(principalsStr, ",") - for i, principal := range principals { - principals[i] = strings.TrimSpace(principal) - } - - certType, err := cmd.Flags().GetString("certType") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - if !isValidCertType(infisicalSdkUtil.SshCertType(certType)) { - util.HandleError(fmt.Errorf("invalid certType: %s", certType), - "Valid values: user, host") - } - - ttl, err := cmd.Flags().GetString("ttl") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - keyId, err := cmd.Flags().GetString("keyId") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - outFilePath, err := cmd.Flags().GetString("outFilePath") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - var ( - outputDir string - signedKeyPath string - ) - - if outFilePath == "" { - // Use current working directory - if err != nil { - util.HandleError(err, "Failed to get current working directory") - } - - // check if public key path exists - if publicKeyFilePath == "" { - util.PrintErrorMessageAndExit("--outFilePath must be specified when --publicKeyFilePath is not provided") - } - - outputDir = filepath.Dir(publicKeyFilePath) - // Derive the base name by removing "-cert.pub" - baseName := strings.TrimSuffix(filepath.Base(publicKeyFilePath), ".pub") - signedKeyPath = filepath.Join(outputDir, baseName+"-cert.pub") - } else { - // Expand ~ to home directory if present - if strings.HasPrefix(outFilePath, "~") { - homeDir, err := os.UserHomeDir() - if err != nil { - util.HandleError(err, "Failed to resolve home directory") - } - outFilePath = strings.Replace(outFilePath, "~", homeDir, 1) - } - - // Check if outFilePath ends with "-cert.pub" - if !strings.HasSuffix(outFilePath, "-cert.pub") { - util.PrintErrorMessageAndExit("--outFilePath must end with -cert.pub") - } - - // Extract the directory from outFilePath - outputDir = filepath.Dir(outFilePath) - - // Validate the output directory - info, err := os.Stat(outputDir) - if os.IsNotExist(err) { - // Directory does not exist; attempt to create it - err = os.MkdirAll(outputDir, 0755) - if err != nil { - util.HandleError(err, "Failed to create output directory") - } - } else if err != nil { - // Other errors accessing the directory - util.HandleError(err, "Failed to access output directory") - } else if !info.IsDir() { - // Path exists but is not a directory - util.PrintErrorMessageAndExit("The provided --outFilePath's directory is not valid") - } - - signedKeyPath = outFilePath - } - - customHeaders, err := util.GetInfisicalCustomHeadersMap() - if err != nil { - util.HandleError(err, "Unable to get custom headers") - } - - infisicalClient := infisicalSdk.NewInfisicalClient(context.Background(), infisicalSdk.Config{ - SiteUrl: config.INFISICAL_URL, - UserAgent: api.USER_AGENT, - AutoTokenRefresh: false, - CustomHeaders: customHeaders, - }) - infisicalClient.Auth().SetAccessToken(infisicalToken) - - creds, err := infisicalClient.Ssh().SignKey(infisicalSdk.SignSshPublicKeyOptions{ - CertificateTemplateID: certificateTemplateId, - PublicKey: publicKey, - Principals: principals, - CertType: infisicalSdkUtil.SshCertType(certType), - TTL: ttl, - KeyID: keyId, - }) - - if err != nil { - util.HandleError(err, "Failed to sign SSH public key") - } - - err = writeToFile(signedKeyPath, creds.SignedKey, 0644) - if err != nil { - util.HandleError(err, "Failed to write Signed Key to file") - } - - fmt.Println("Successfully wrote SSH certificate to:", signedKeyPath) -} - -func sshConnect(cmd *cobra.Command, args []string) { - token, err := util.GetInfisicalToken(cmd) - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - var infisicalToken string - - if token != nil && (token.Type == util.SERVICE_TOKEN_IDENTIFIER || token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER) { - infisicalToken = token.Token - } else { - util.RequireLogin() - - loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true) - if err != nil { - util.HandleError(err, "Unable to authenticate") - } - - if loggedInUserDetails.LoginExpired { - loggedInUserDetails = util.EstablishUserLoginSession() - } - infisicalToken = loggedInUserDetails.UserCredentials.JTWToken - } - - writeHostCaToFile, err := cmd.Flags().GetBool("write-host-ca-to-file") - if err != nil { - util.HandleError(err, "Unable to parse --write-host-ca-to-file flag") - } - - outFilePath, err := cmd.Flags().GetString("out-file-path") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - hostname, _ := cmd.Flags().GetString("hostname") - loginUser, _ := cmd.Flags().GetString("login-user") - - var outputDir, privateKeyPath, publicKeyPath, signedKeyPath string - if outFilePath != "" { - if strings.HasPrefix(outFilePath, "~") { - homeDir, err := os.UserHomeDir() - if err != nil { - util.HandleError(err, "Failed to resolve home directory") - } - outFilePath = strings.Replace(outFilePath, "~", homeDir, 1) - } - - if strings.HasSuffix(outFilePath, "-cert.pub") { - signedKeyPath = outFilePath - baseName := strings.TrimSuffix(filepath.Base(outFilePath), "-cert.pub") - outputDir = filepath.Dir(outFilePath) - privateKeyPath = filepath.Join(outputDir, baseName) - publicKeyPath = filepath.Join(outputDir, baseName+".pub") - } else { - outputDir = outFilePath - info, err := os.Stat(outputDir) - if os.IsNotExist(err) { - err = os.MkdirAll(outputDir, 0755) - if err != nil { - util.HandleError(err, "Failed to create output directory") - } - } else if err != nil { - util.HandleError(err, "Failed to access output directory") - } else if !info.IsDir() { - util.PrintErrorMessageAndExit("The provided --outFilePath is not a directory") - } - fileName := "id_ed25519" - privateKeyPath = filepath.Join(outputDir, fileName) - publicKeyPath = filepath.Join(outputDir, fileName+".pub") - signedKeyPath = filepath.Join(outputDir, fileName+"-cert.pub") - } - - if privateKeyPath == "" || publicKeyPath == "" || signedKeyPath == "" { - util.PrintErrorMessageAndExit("Failed to resolve file paths for writing credentials") - } - } - - customHeaders, err := util.GetInfisicalCustomHeadersMap() - if err != nil { - util.HandleError(err, "Unable to get custom headers") - } - - infisicalClient := infisicalSdk.NewInfisicalClient(context.Background(), infisicalSdk.Config{ - SiteUrl: config.INFISICAL_URL, - UserAgent: api.USER_AGENT, - AutoTokenRefresh: false, - CustomHeaders: customHeaders, - }) - infisicalClient.Auth().SetAccessToken(infisicalToken) - - // Fetch SSH Hosts - hosts, err := infisicalClient.Ssh().GetSshHosts(infisicalSdk.GetSshHostsOptions{}) - if err != nil { - util.HandleError(err, "Failed to fetch SSH hosts") - } - if len(hosts) == 0 { - util.PrintErrorMessageAndExit("You do not have access to any SSH hosts") - } - - var selectedHost = hosts[0] - if hostname != "" { - foundHost := false - for _, h := range hosts { - if h.Hostname == hostname { - selectedHost = h - foundHost = true - break - } - } - if !foundHost { - util.PrintErrorMessageAndExit("Specified --hostname not found or not accessible") - } - } else { - hostNames := make([]string, len(hosts)) - for i, h := range hosts { - if h.Alias != "" { - hostNames[i] = h.Alias - } else { - hostNames[i] = h.Hostname - } - } - - hostPrompt := promptui.Select{ - Label: "Select an SSH Host", - Items: hostNames, - Size: 10, - } - - hostIdx, _, err := hostPrompt.Run() - if err != nil { - util.HandleError(err, "Prompt failed") - } - - selectedHost = hosts[hostIdx] - } - - var selectedLoginUser string - if loginUser != "" { - foundLoginUser := false - for _, m := range selectedHost.LoginMappings { - if m.LoginUser == loginUser { - selectedLoginUser = loginUser - foundLoginUser = true - break - } - } - if !foundLoginUser { - util.PrintErrorMessageAndExit("Specified --loginUser not valid for selected host") - } - } else { - if len(selectedHost.LoginMappings) == 0 { - util.PrintErrorMessageAndExit("No login users available for selected host") - } - loginUsers := make([]string, len(selectedHost.LoginMappings)) - for i, m := range selectedHost.LoginMappings { - loginUsers[i] = m.LoginUser - } - loginPrompt := promptui.Select{ - Label: "Select Login User", - Items: loginUsers, - Size: 5, - } - loginIdx, _, err := loginPrompt.Run() - if err != nil { - util.HandleError(err, "Prompt failed") - } - selectedLoginUser = selectedHost.LoginMappings[loginIdx].LoginUser - } - - // Issue SSH creds for host - creds, err := infisicalClient.Ssh().IssueSshHostUserCert(selectedHost.ID, infisicalSdk.IssueSshHostUserCertOptions{ - LoginUser: selectedLoginUser, - }) - if err != nil { - util.HandleError(err, "Failed to issue SSH credentials") - } - - // Write Host CA public key to known_hosts if enabled - if writeHostCaToFile { - hostCaPublicKey, err := infisicalClient.Ssh().GetSshHostHostCaPublicKey(selectedHost.ID) - if err != nil { - util.HandleError(err, "Failed to fetch Host CA public key") - } - - // Build @cert-authority line - caLine := fmt.Sprintf("@cert-authority %s %s\n", selectedHost.Hostname, strings.TrimSpace(hostCaPublicKey)) - - // Determine known_hosts path - sshDir := filepath.Join(os.Getenv("HOME"), ".ssh") - knownHostsPath := filepath.Join(sshDir, "known_hosts") - - // Ensure ~/.ssh exists - if _, err := os.Stat(sshDir); os.IsNotExist(err) { - if err := os.MkdirAll(sshDir, 0700); err != nil { - util.HandleError(err, "Failed to create ~/.ssh directory") - } - } - - // Check if CA line already exists - knownHostsBytes, _ := os.ReadFile(knownHostsPath) - if !strings.Contains(string(knownHostsBytes), caLine) { - f, err := os.OpenFile(knownHostsPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600) - if err != nil { - util.HandleError(err, "Failed to open known_hosts file") - } - defer f.Close() - - if _, err := f.WriteString(caLine); err != nil { - util.HandleError(err, "Failed to write Host CA to known_hosts") - } - - fmt.Printf("Successfully wrote Host CA entry to %s\n", knownHostsPath) - } - } - - if outFilePath != "" { - err = writeToFile(privateKeyPath, creds.PrivateKey, 0600) - if err != nil { - util.HandleError(err, "Failed to write private key") - } - err = writeToFile(publicKeyPath, creds.PublicKey, 0644) - if err != nil { - util.HandleError(err, "Failed to write public key") - } - err = writeToFile(signedKeyPath, creds.SignedKey, 0644) - if err != nil { - util.HandleError(err, "Failed to write signed cert") - } - fmt.Printf("Successfully wrote credentials to %s, %s, and %s\n", privateKeyPath, publicKeyPath, signedKeyPath) - return - } - - // Load credentials into SSH agent - err = addCredentialsToAgent(creds.PrivateKey, creds.SignedKey) - if err != nil { - util.HandleError(err, "Failed to add credentials to SSH agent") - } - fmt.Println("✔ SSH credentials successfully added to agent") - - // Connect to host using system ssh and agent - target := fmt.Sprintf("%s@%s", selectedLoginUser, selectedHost.Hostname) - fmt.Printf("Connecting to %s...\n", target) - - sshCmd := exec.Command("ssh", target) - sshCmd.Stdin = os.Stdin - sshCmd.Stdout = os.Stdout - sshCmd.Stderr = os.Stderr - - err = sshCmd.Run() - if err != nil { - util.HandleError(err, "SSH connection failed") - } -} - -func sshAddHost(cmd *cobra.Command, args []string) { - - token, err := util.GetInfisicalToken(cmd) - if err != nil { - util.HandleError(err, "Unable to parse token") - } - - var infisicalToken string - if token != nil && (token.Type == util.SERVICE_TOKEN_IDENTIFIER || token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER) { - infisicalToken = token.Token - } else { - util.RequireLogin() - - loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true) - if err != nil { - util.HandleError(err, "Unable to authenticate") - } - if loggedInUserDetails.LoginExpired { - loggedInUserDetails = util.EstablishUserLoginSession() - } - infisicalToken = loggedInUserDetails.UserCredentials.JTWToken - } - - projectId, err := cmd.Flags().GetString("projectId") - if err != nil { - util.HandleError(err, "Unable to parse --projectId flag") - } - if projectId == "" { - util.PrintErrorMessageAndExit("You must provide --projectId") - } - - hostname, err := cmd.Flags().GetString("hostname") - if err != nil { - util.HandleError(err, "Unable to parse --hostname flag") - } - if hostname == "" { - util.PrintErrorMessageAndExit("You must provide --hostname") - } - - alias, err := cmd.Flags().GetString("alias") - if err != nil { - util.HandleError(err, "Unable to parse --alias flag") - } - - // if alias == "" { - // util.PrintErrorMessageAndExit("You must provide --alias") - // } - - writeUserCaToFile, err := cmd.Flags().GetBool("write-user-ca-to-file") - if err != nil { - util.HandleError(err, "Unable to parse --write-user-ca-to-file flag") - } - - userCaOutFilePath, err := cmd.Flags().GetString("user-ca-out-file-path") - if err != nil { - util.HandleError(err, "Unable to parse --user-ca-out-file-path flag") - } - - writeHostCertToFile, err := cmd.Flags().GetBool("write-host-cert-to-file") - if err != nil { - util.HandleError(err, "Unable to parse --write-host-cert-to-file flag") - } - - configureSshd, err := cmd.Flags().GetBool("configure-sshd") - if err != nil { - util.HandleError(err, "Unable to parse --configure-sshd flag") - } - - forceOverwrite, err := cmd.Flags().GetBool("force") - if err != nil { - util.HandleError(err, "Unable to parse --force flag") - } - - if configureSshd && (!writeUserCaToFile || !writeHostCertToFile) { - util.PrintErrorMessageAndExit("--configure-sshd requires both --write-user-ca-to-file and --write-host-cert-to-file to also be set") - } - - // Pre-check for file overwrites before proceeding - if writeUserCaToFile { - if strings.HasPrefix(userCaOutFilePath, "~") { - homeDir, err := os.UserHomeDir() - if err != nil { - util.HandleError(err, "Unable to resolve ~ in user-ca-out-file-path") - } - userCaOutFilePath = strings.Replace(userCaOutFilePath, "~", homeDir, 1) - } - if _, err := os.Stat(userCaOutFilePath); err == nil && !forceOverwrite { - util.PrintErrorMessageAndExit("File already exists at " + userCaOutFilePath + ". Use --force to overwrite.") - } - } - - keyTypes := []string{"ed25519", "ecdsa", "rsa"} - var hostKeyPath, certOutPath, hostPrivateKeyPath string - if writeHostCertToFile { - for _, keyType := range keyTypes { - pub := fmt.Sprintf("/etc/ssh/ssh_host_%s_key.pub", keyType) - cert := fmt.Sprintf("/etc/ssh/ssh_host_%s_key-cert.pub", keyType) - priv := fmt.Sprintf("/etc/ssh/ssh_host_%s_key", keyType) - - if _, err := os.Stat(pub); err == nil { - hostKeyPath = pub - certOutPath = cert - hostPrivateKeyPath = priv - break - } - } - - if hostKeyPath == "" { - util.PrintErrorMessageAndExit("No supported SSH host public key found at /etc/ssh") - } - - if _, err := os.Stat(certOutPath); err == nil && !forceOverwrite { - util.PrintErrorMessageAndExit("File already exists at " + certOutPath + ". Use --force to overwrite.") - } - } - - if configureSshd { - sshdConfig := "/etc/ssh/sshd_config" - existing, err := os.ReadFile(sshdConfig) - if err != nil { - util.HandleError(err, "Failed to read sshd_config") - } - configLines := []string{ - "TrustedUserCAKeys " + userCaOutFilePath, - "HostKey " + hostPrivateKeyPath, - "HostCertificate " + certOutPath, - } - for _, line := range configLines { - for _, existingLine := range strings.Split(string(existing), "\n") { - trimmed := strings.TrimSpace(existingLine) - if trimmed == line && !strings.HasPrefix(trimmed, "#") && !forceOverwrite { - util.PrintErrorMessageAndExit("sshd_config already contains: " + line + ". Use --force to overwrite.") - } - } - } - } - - customHeaders, err := util.GetInfisicalCustomHeadersMap() - if err != nil { - util.HandleError(err, "Unable to get custom headers") - } - - client := infisicalSdk.NewInfisicalClient(context.Background(), infisicalSdk.Config{ - SiteUrl: config.INFISICAL_URL, - UserAgent: api.USER_AGENT, - AutoTokenRefresh: false, - CustomHeaders: customHeaders, - }) - client.Auth().SetAccessToken(infisicalToken) - - host, err := client.Ssh().AddSshHost(infisicalSdk.AddSshHostOptions{ - ProjectID: projectId, - Hostname: hostname, - Alias: alias, - }) - if err != nil { - util.HandleError(err, "Failed to register SSH host") - } - - fmt.Println("✅ Successfully registered host:", host.Hostname) - - if writeUserCaToFile { - publicKey, err := client.Ssh().GetSshHostUserCaPublicKey(host.ID) - if err != nil { - util.HandleError(err, "Failed to fetch associated User CA public key") - } - - if err := writeToFile(userCaOutFilePath, publicKey, 0644); err != nil { - util.HandleError(err, "Failed to write User CA public key to file") - } - - fmt.Println("📁 Wrote User CA public key to:", userCaOutFilePath) - } - - if writeHostCertToFile { - pubKeyBytes, err := os.ReadFile(hostKeyPath) - if err != nil { - util.HandleError(err, "Failed to read SSH host public key") - } - res, err := client.Ssh().IssueSshHostHostCert(host.ID, infisicalSdk.IssueSshHostHostCertOptions{ - PublicKey: string(pubKeyBytes), - }) - if err != nil { - util.HandleError(err, "Failed to issue SSH host certificate") - } - if err := writeToFile(certOutPath, res.SignedKey, 0644); err != nil { - util.HandleError(err, "Failed to write SSH host certificate to file") - } - fmt.Println("📁 Wrote host certificate to:", certOutPath) - } - - if configureSshd { - sshdConfig := "/etc/ssh/sshd_config" - contentBytes, err := os.ReadFile(sshdConfig) - if err != nil { - util.HandleError(err, "Failed to read sshd_config") - } - lines := strings.Split(string(contentBytes), "\n") - - configMap := map[string]string{ - "TrustedUserCAKeys": userCaOutFilePath, - "HostKey": hostPrivateKeyPath, - "HostCertificate": certOutPath, - } - - seenKeys := map[string]bool{} - for i, line := range lines { - trimmed := strings.TrimSpace(line) - for key, value := range configMap { - if strings.HasPrefix(trimmed, key+" ") { - seenKeys[key] = true - if strings.HasPrefix(trimmed, "#") || forceOverwrite { - lines[i] = fmt.Sprintf("%s %s", key, value) - } else { - util.PrintErrorMessageAndExit("sshd_config already contains: " + trimmed + ". Use --force to overwrite.") - } - } - } - } - - // Append missing lines - for key, value := range configMap { - if !seenKeys[key] { - lines = append(lines, fmt.Sprintf("%s %s", key, value)) - } - } - - // Write back to file - if err := os.WriteFile(sshdConfig, []byte(strings.Join(lines, "\n")), 0644); err != nil { - util.HandleError(err, "Failed to update sshd_config") - } - fmt.Println("📄 Updated sshd_config entries") - } -} - -func init() { - sshSignKeyCmd.Flags().String("token", "", "Issue SSH certificate using machine identity access token") - sshSignKeyCmd.Flags().String("certificateTemplateId", "", "The ID of the SSH certificate template to issue the SSH certificate for") - sshSignKeyCmd.Flags().String("publicKey", "", "The public key to sign") - sshSignKeyCmd.Flags().String("publicKeyFilePath", "", "The file path to the public key file to sign") - sshSignKeyCmd.Flags().String("outFilePath", "", "The path to write the SSH certificate to such as ~/.ssh/id_rsa-cert.pub. If not provided, the credentials will be saved to the directory of the specified public key file path or the current working directory") - sshSignKeyCmd.Flags().String("principals", "", "The principals that the certificate should be signed for") - sshSignKeyCmd.Flags().String("certType", string(infisicalSdkUtil.UserCert), "The cert type for the created certificate") - sshSignKeyCmd.Flags().String("ttl", "", "The ttl for the created certificate") - sshSignKeyCmd.Flags().String("keyId", "", "The keyId that the created certificate should have") - sshCmd.AddCommand(sshSignKeyCmd) - - sshIssueCredentialsCmd.Flags().String("token", "", "Issue SSH credentials using machine identity access token") - sshIssueCredentialsCmd.Flags().String("certificateTemplateId", "", "The ID of the SSH certificate template to issue SSH credentials for") - sshIssueCredentialsCmd.Flags().String("principals", "", "The principals to issue SSH credentials for") - sshIssueCredentialsCmd.Flags().String("keyAlgorithm", string(infisicalSdkUtil.RSA2048), "The key algorithm to issue SSH credentials for") - sshIssueCredentialsCmd.Flags().String("certType", string(infisicalSdkUtil.UserCert), "The cert type to issue SSH credentials for") - sshIssueCredentialsCmd.Flags().String("ttl", "", "The ttl to issue SSH credentials for") - sshIssueCredentialsCmd.Flags().String("keyId", "", "The keyId to issue SSH credentials for") - sshIssueCredentialsCmd.Flags().String("outFilePath", "", "The path to write the SSH credentials to such as ~/.ssh, ./some_folder, ./some_folder/id_rsa-cert.pub. If not provided, the credentials will be saved to the current working directory") - sshIssueCredentialsCmd.Flags().Bool("addToAgent", false, "Whether to add issued SSH credentials to the SSH agent") - sshCmd.AddCommand(sshIssueCredentialsCmd) - - sshConnectCmd.Flags().String("token", "", "Use a machine identity access token") - sshConnectCmd.Flags().Bool("write-host-ca-to-file", true, "Write Host CA public key to ~/.ssh/known_hosts as a separate entry if doesn't already exist") - sshConnectCmd.Flags().String("hostname", "", "Hostname of the SSH host to connect to") - sshConnectCmd.Flags().String("login-user", "", "Login user for the SSH connection") - sshConnectCmd.Flags().String("out-file-path", "", "The path to write the SSH credentials to such as ~/.ssh, ./some_folder, ./some_folder/id_rsa-cert.pub. If not provided, the credentials will be added to the SSH agent and used to establish an interactive SSH connection") - sshCmd.AddCommand(sshConnectCmd) - - sshAddHostCmd.Flags().String("token", "", "Use a machine identity access token") - sshAddHostCmd.Flags().String("projectId", "", "Project ID the host belongs to (required)") - sshAddHostCmd.Flags().String("hostname", "", "Hostname of the SSH host (required)") - sshAddHostCmd.Flags().String("alias", "", "Alias for the SSH host") - sshAddHostCmd.Flags().Bool("write-user-ca-to-file", false, "Write User CA public key to /etc/ssh/infisical_user_ca.pub") - sshAddHostCmd.Flags().String("user-ca-out-file-path", "/etc/ssh/infisical_user_ca.pub", "Custom file path to write the User CA public key") - sshAddHostCmd.Flags().Bool("write-host-cert-to-file", false, "Write SSH host certificate to /etc/ssh/ssh_host__key-cert.pub") - sshAddHostCmd.Flags().Bool("configure-sshd", false, "Update `TrustedUserCAKeys`, `HostKey`, and `HostCertificate` in the `/etc/ssh/sshd_config` file") - sshAddHostCmd.Flags().Bool("force", false, "Force overwrite of existing certificate files as part of `--write-user-ca-to-file` and `--write-host-cert-to-file`") - - sshCmd.AddCommand(sshAddHostCmd) - - rootCmd.AddCommand(sshCmd) -} diff --git a/cli/packages/cmd/token.go b/cli/packages/cmd/token.go deleted file mode 100644 index 4e568cb85..000000000 --- a/cli/packages/cmd/token.go +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright (c) 2023 Infisical Inc. -*/ -package cmd - -import ( - "strings" - "time" - - "github.com/Infisical/infisical-merge/packages/util" - "github.com/fatih/color" - "github.com/spf13/cobra" -) - -var tokenCmd = &cobra.Command{ - Use: "token", - Short: "Manage your access tokens", - DisableFlagsInUseLine: true, - Example: "infisical token", - Args: cobra.ExactArgs(0), - PreRun: func(cmd *cobra.Command, args []string) { - util.RequireLogin() - }, - Run: func(cmd *cobra.Command, args []string) { - }, -} - -var tokenRenewCmd = &cobra.Command{ - Use: "renew [token]", - Short: "Used to renew your universal auth access token", - DisableFlagsInUseLine: true, - Example: "infisical token renew ", - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - // args[0] will be the from your command call - token := args[0] - - if strings.HasPrefix(token, "st.") { - util.PrintErrorMessageAndExit("You are trying to renew a service token. You can only renew universal auth access tokens.") - } - - renewedAccessToken, err := util.RenewMachineIdentityAccessToken(token) - - if err != nil { - util.HandleError(err, "Unable to renew token") - } - - boldGreen := color.New(color.FgGreen).Add(color.Bold) - time.Sleep(time.Second * 1) - boldGreen.Printf(">>>> Successfully renewed token!\n\n") - boldGreen.Printf("Renewed Access Token:\n%v", renewedAccessToken) - - plainBold := color.New(color.Bold) - plainBold.Println("\n\nYou can use the new access token to authenticate through other commands in the CLI.") - - }, -} - -func init() { - tokenCmd.AddCommand(tokenRenewCmd) - - rootCmd.AddCommand(tokenCmd) -} diff --git a/cli/packages/cmd/tokens.go b/cli/packages/cmd/tokens.go deleted file mode 100644 index a2e445239..000000000 --- a/cli/packages/cmd/tokens.go +++ /dev/null @@ -1,193 +0,0 @@ -/* -Copyright (c) 2023 Infisical Inc. -*/ -package cmd - -import ( - "crypto/rand" - "encoding/base64" - "encoding/hex" - "fmt" - "strings" - - "github.com/Infisical/infisical-merge/packages/api" - "github.com/Infisical/infisical-merge/packages/crypto" - "github.com/Infisical/infisical-merge/packages/util" - "github.com/spf13/cobra" -) - -var tokensCmd = &cobra.Command{ - Use: "service-token", - Short: "Manage service tokens", - DisableFlagsInUseLine: true, - Example: "infisical service-token", - Args: cobra.ExactArgs(0), - PreRun: func(cmd *cobra.Command, args []string) { - util.RequireLogin() - }, - Run: func(cmd *cobra.Command, args []string) { - }, -} - -var tokensCreateCmd = &cobra.Command{ - Use: "create", - Short: "Used to create service tokens", - DisableFlagsInUseLine: true, - Example: "infisical service-token create", - Args: cobra.ExactArgs(0), - PreRun: func(cmd *cobra.Command, args []string) { - util.RequireLogin() - }, - Run: func(cmd *cobra.Command, args []string) { - // get plain text workspace key - loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true) - - if err != nil { - util.HandleError(err, "Unable to retrieve your logged in your details. Please login in then try again") - } - - if loggedInUserDetails.LoginExpired { - loggedInUserDetails = util.EstablishUserLoginSession() - } - - tokenOnly, err := cmd.Flags().GetBool("token-only") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - workspaceId, err := cmd.Flags().GetString("projectId") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - if workspaceId == "" { - configFile, err := util.GetWorkSpaceFromFile() - if err != nil { - util.PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag") - } - workspaceId = configFile.WorkspaceId - } - - serviceTokenName, err := cmd.Flags().GetString("name") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - expireSeconds, err := cmd.Flags().GetInt("expiry-seconds") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - scopes, err := cmd.Flags().GetStringSlice("scope") - if err != nil { - util.HandleError(err, "Unable to parse flag") - } - - if len(scopes) == 0 { - util.PrintErrorMessageAndExit("You must define the environments and paths your service token should have access to via the --scope flag") - } - - permissions := []api.ScopePermission{} - - for _, scope := range scopes { - parts := strings.Split(scope, ":") - - if len(parts) != 2 { - fmt.Println("--scope flag is malformed. Each scope flag should be in the following format: :") - return - } - - permissions = append(permissions, api.ScopePermission{Environment: parts[0], SecretPath: parts[1]}) - } - - accessLevels, err := cmd.Flags().GetStringSlice("access-level") - if err != nil { - util.HandleError(err, "Unable to parse flag accessLevels") - } - - if len(accessLevels) == 0 { - util.PrintErrorMessageAndExit("You must define whether your service token can be used to read and or write via the --access-level flag") - } - - for _, accessLevel := range accessLevels { - if accessLevel != "read" && accessLevel != "write" { - util.PrintErrorMessageAndExit("--access-level can only be of values read and write") - } - } - - workspaceKey, err := util.GetPlainTextWorkspaceKey(loggedInUserDetails.UserCredentials.JTWToken, loggedInUserDetails.UserCredentials.PrivateKey, workspaceId) - if err != nil { - util.HandleError(err, "Unable to get workspace key needed to create service token") - } - - newWorkspaceEncryptionKey := make([]byte, 16) - _, err = rand.Read(newWorkspaceEncryptionKey) - if err != nil { - util.HandleError(err) - } - - newWorkspaceEncryptionKeyHexFormat := hex.EncodeToString(newWorkspaceEncryptionKey) - - // encrypt the workspace key symmetrically - encryptedDetails, err := crypto.EncryptSymmetric(workspaceKey, []byte(newWorkspaceEncryptionKeyHexFormat)) - if err != nil { - util.HandleError(err) - } - - // make a call to the api to save the encrypted symmetric key details - httpClient, err := util.GetRestyClientWithCustomHeaders() - if err != nil { - util.HandleError(err, "Unable to get resty client with custom headers") - } - - httpClient.SetAuthToken(loggedInUserDetails.UserCredentials.JTWToken). - SetHeader("Accept", "application/json") - - createServiceTokenResponse, err := api.CallCreateServiceToken(httpClient, api.CreateServiceTokenRequest{ - Name: serviceTokenName, - WorkspaceId: workspaceId, - Scopes: permissions, - ExpiresIn: expireSeconds, - EncryptedKey: base64.StdEncoding.EncodeToString(encryptedDetails.CipherText), - Iv: base64.StdEncoding.EncodeToString(encryptedDetails.Nonce), - Tag: base64.StdEncoding.EncodeToString(encryptedDetails.AuthTag), - RandomBytes: newWorkspaceEncryptionKeyHexFormat, - Permissions: accessLevels, - }) - - if err != nil { - util.HandleError(err, "Unable to create service token") - } - - serviceToken := createServiceTokenResponse.ServiceToken + "." + newWorkspaceEncryptionKeyHexFormat - - if tokenOnly { - fmt.Println(serviceToken) - } else { - printablePermission := []string{} - for _, permission := range permissions { - printablePermission = append(printablePermission, fmt.Sprintf("([environment: %v] [path: %v])", permission.Environment, permission.SecretPath)) - } - - fmt.Printf("New service token created\n") - fmt.Printf("Name: %v\n", serviceTokenName) - fmt.Printf("Project ID: %v\n", workspaceId) - fmt.Printf("Access type: [%v]\n", strings.Join(accessLevels, ", ")) - fmt.Printf("Permission(s): %v\n", strings.Join(printablePermission, ", ")) - fmt.Printf("Service Token: %v\n", serviceToken) - } - }, -} - -func init() { - tokensCreateCmd.Flags().String("projectId", "", "The project ID you'd like to create the service token for. Default: will use linked Infisical project in .infisical.json") - tokensCreateCmd.Flags().StringSliceP("scope", "s", []string{}, "Environment and secret path. Example format: :") - tokensCreateCmd.Flags().StringP("name", "n", "Service token generated via CLI", "Service token name") - tokensCreateCmd.Flags().StringSliceP("access-level", "a", []string{}, "The type of access the service token should have. Can be 'read' and or 'write'") - tokensCreateCmd.Flags().Bool("token-only", false, "When true, only the service token will be printed") - tokensCreateCmd.Flags().IntP("expiry-seconds", "e", 86400, "Set the service token's expiration time in seconds from now. To never expire set to zero. Default: 1 day ") - - tokensCmd.AddCommand(tokensCreateCmd) - - rootCmd.AddCommand(tokensCmd) -} diff --git a/cli/packages/cmd/user.go b/cli/packages/cmd/user.go deleted file mode 100644 index 3b0970403..000000000 --- a/cli/packages/cmd/user.go +++ /dev/null @@ -1,325 +0,0 @@ -package cmd - -import ( - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "net/url" - "strings" - - "github.com/Infisical/infisical-merge/packages/config" - "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" -) - -var userCmd = &cobra.Command{ - Use: "user", - Short: "Used to manage local user credentials", - DisableFlagsInUseLine: true, - Example: "infisical user", - Args: cobra.ExactArgs(0), - Run: func(cmd *cobra.Command, args []string) { - cmd.Help() - }, -} - -var switchCmd = &cobra.Command{ - Use: "switch", - Short: "Used to switch between Infisical profiles", - DisableFlagsInUseLine: true, - Example: "infisical switch", - Args: cobra.ExactArgs(0), - PreRun: func(cmd *cobra.Command, args []string) { - util.RequireLogin() - }, - Run: func(cmd *cobra.Command, args []string) { - //get previous logged in profiles - loggedInProfiles, err := getLoggedInUsers() - if err != nil { - util.HandleError(err, "[infisical user switch]: Unable to get logged Profiles") - } - - //prompt user - profile, err := LoggedInUsersPrompt(loggedInProfiles) - if err != nil { - util.HandleError(err, "[infisical user switch]: Prompt error") - } - - //write to config file - configFile, err := util.GetConfigFile() - if err != nil { - util.HandleError(err, "[infisical user switch]: Unable to get config file") - } - - configFile.LoggedInUserEmail = profile - - //set logged in user domain - ok := util.ConfigContainsEmail(configFile.LoggedInUsers, profile) - - if !ok { - //profile not in loggedInUsers - configFile.LoggedInUsers = append(configFile.LoggedInUsers, models.LoggedInUser{ - Email: profile, - Domain: config.INFISICAL_URL, - }) - //set logged in user domain - configFile.LoggedInUserDomain = config.INFISICAL_URL - - } else { - //exists, set logged in user domain - for _, v := range configFile.LoggedInUsers { - if profile == v.Email { - configFile.LoggedInUserDomain = v.Domain - break - } - } - } - - err = util.WriteConfigFile(&configFile) - if err != nil { - util.HandleError(err, "") - } - - Telemetry.CaptureEvent("cli-command:user switch", posthog.NewProperties().Set("numberOfLoggedInProfiles", len(loggedInProfiles)).Set("version", util.CLI_VERSION)) - }, -} - -var userGetCmd = &cobra.Command{ - Use: "get", - Short: "Used to get properties of an Infisical profile", - DisableFlagsInUseLine: true, - Example: "infisical user get", - Args: cobra.ExactArgs(0), - Run: func(cmd *cobra.Command, args []string) { - cmd.Help() - }, -} - -var userGetTokenCmd = &cobra.Command{ - Use: "token", - Short: "Used to get the access token of an Infisical user", - DisableFlagsInUseLine: true, - Example: "infisical user get token", - Args: cobra.ExactArgs(0), - PreRun: func(cmd *cobra.Command, args []string) { - util.RequireLogin() - }, - Run: func(cmd *cobra.Command, args []string) { - loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true) - if loggedInUserDetails.LoginExpired { - loggedInUserDetails = util.EstablishUserLoginSession() - } - - plain, err := cmd.Flags().GetBool("plain") - if err != nil { - util.HandleError(err, "[infisical user get token]: Unable to get plain flag") - } - - if err != nil { - util.HandleError(err, "[infisical user get token]: Unable to get logged in user token") - } - - tokenParts := strings.Split(loggedInUserDetails.UserCredentials.JTWToken, ".") - if len(tokenParts) != 3 { - util.HandleError(errors.New("invalid token format"), "[infisical user get token]: Invalid token format") - } - - payload, err := base64.RawURLEncoding.DecodeString(tokenParts[1]) - if err != nil { - util.HandleError(err, "[infisical user get token]: Unable to decode token payload") - } - - var tokenPayload struct { - TokenVersionId string `json:"tokenVersionId"` - } - if err := json.Unmarshal(payload, &tokenPayload); err != nil { - util.HandleError(err, "[infisical user get token]: Unable to parse token payload") - } - - if plain { - fmt.Println(loggedInUserDetails.UserCredentials.JTWToken) - } else { - fmt.Println("Session ID:", tokenPayload.TokenVersionId) - fmt.Println("Token:", loggedInUserDetails.UserCredentials.JTWToken) - } - }, -} - -var updateCmd = &cobra.Command{ - Use: "update", - Short: "Used to update properties of an Infisical profile", - DisableFlagsInUseLine: true, - Example: "infisical user update", - Args: cobra.ExactArgs(0), - Run: func(cmd *cobra.Command, args []string) { - cmd.Help() - }, -} - -var domainCmd = &cobra.Command{ - Use: "domain", - Short: "Used to update the domain of an Infisical profile", - DisableFlagsInUseLine: true, - Example: "infisical user update domain", - Args: cobra.ExactArgs(0), - PreRun: func(cmd *cobra.Command, args []string) { - util.RequireLogin() - }, - Run: func(cmd *cobra.Command, args []string) { - //prompt for profiles selection - loggedInProfiles, err := getLoggedInUsers() - if err != nil { - util.HandleError(err, "[infisical user update domain]: Unable to get logged Profiles") - } - - //prompt user - profile, err := LoggedInUsersPrompt(loggedInProfiles) - if err != nil { - util.HandleError(err, "[infisical user update domain]: Prompt error") - } - - domain := "" - domainQuery := true - if config.INFISICAL_URL_MANUAL_OVERRIDE != fmt.Sprintf("%s/api", util.INFISICAL_DEFAULT_EU_URL) && config.INFISICAL_URL_MANUAL_OVERRIDE != fmt.Sprintf("%s/api", util.INFISICAL_DEFAULT_US_URL) { - - override, err := DomainOverridePrompt() - if err != nil { - util.HandleError(err, "[infisical user update domain]: Domain override prompt error") - } - - if !override { - domainQuery = false - domain = config.INFISICAL_URL_MANUAL_OVERRIDE - } - - } - - if domainQuery { - //prompt to update domain - domain, err = NewDomainPrompt() - if err != nil { - util.HandleError(err, "[infisical user update domain]: Prompt error") - } - } - - //write to config file - configFile, err := util.GetConfigFile() - if err != nil { - util.HandleError(err, "[infisical user update domain]: Unable to get config file") - } - - //check if profile in logged in profiles - - //if not add new profile loggedInUsers - //else update profile from loggedinUsers slice - ok := util.ConfigContainsEmail(configFile.LoggedInUsers, profile) - if !ok { - configFile.LoggedInUsers = append(configFile.LoggedInUsers, models.LoggedInUser{ - Email: profile, - Domain: domain, - }) - } else { - //exists, set logged in user domain - for idx, v := range configFile.LoggedInUsers { - if profile == v.Email { - configFile.LoggedInUsers[idx].Domain = domain //inplace - break - } - } - - } - //check if current loggedinuser is selected profile - //if yes set current domain to changed domain - if configFile.LoggedInUserEmail == profile { - configFile.LoggedInUserDomain = domain - } - - err = util.WriteConfigFile(&configFile) - if err != nil { - util.HandleError(err, "") - } - Telemetry.CaptureEvent("cli-command:user domain", posthog.NewProperties().Set("version", util.CLI_VERSION)) - }, -} - -func init() { - updateCmd.AddCommand(domainCmd) - userCmd.AddCommand(updateCmd) - - userGetTokenCmd.Flags().Bool("plain", false, "print token without formatting") - userGetCmd.AddCommand(userGetTokenCmd) - - userCmd.AddCommand(userGetCmd) - userCmd.AddCommand(switchCmd) - rootCmd.AddCommand(userCmd) -} - -// This returns all logged in user emails from the config file. -// If none, it returns the current logged in user in a slice -func getLoggedInUsers() ([]string, error) { - loggedInProfiles := []string{} - - if util.ConfigFileExists() { - configFile, err := util.GetConfigFile() - if err != nil { - return loggedInProfiles, err - } - - //get logged in profiles - // - if len(configFile.LoggedInUsers) > 0 { - for _, v := range configFile.LoggedInUsers { - loggedInProfiles = append(loggedInProfiles, v.Email) - } - } else { - - loggedInProfiles = append(loggedInProfiles, configFile.LoggedInUserEmail) - } - return loggedInProfiles, nil - } else { - //empty - return loggedInProfiles, errors.New("couldn't retrieve config file") - } -} - -func NewDomainPrompt() (string, error) { - urlValidation := func(input string) error { - _, err := url.ParseRequestURI(input) - if err != nil { - return errors.New("this is an invalid url") - } - return nil - } - - //else run prompt to enter domain - domainPrompt := promptui.Prompt{ - Label: "New Domain", - Validate: urlValidation, - Default: "Example - https://my-self-hosted-instance.com/api", - } - - domain, err := domainPrompt.Run() - if err != nil { - return "", err - } - - return util.AppendAPIEndpoint(domain), nil -} - -func LoggedInUsersPrompt(profiles []string) (string, error) { - prompt := promptui.Select{Label: "Which of your Infisical profiles would you like to use", - Items: profiles, - Size: 7, - } - - idx, _, err := prompt.Run() - if err != nil { - return "", err - } - - return profiles[idx], nil -} diff --git a/cli/packages/cmd/vault.go b/cli/packages/cmd/vault.go deleted file mode 100644 index 6a92ef960..000000000 --- a/cli/packages/cmd/vault.go +++ /dev/null @@ -1,113 +0,0 @@ -/* -Copyright (c) 2023 Infisical Inc. -*/ -package cmd - -import ( - "encoding/base64" - "fmt" - "strings" - - "github.com/Infisical/infisical-merge/packages/util" - "github.com/posthog/posthog-go" - "github.com/rs/zerolog/log" - "github.com/spf13/cobra" -) - -type VaultBackendType struct { - Name string - Description string -} - -var AvailableVaults = []VaultBackendType{ - { - Name: "auto", - Description: "automatically select the system keyring", - }, - { - Name: "file", - Description: "encrypted file vault", - }, -} - -var vaultSetCmd = &cobra.Command{ - Example: `infisical vault set file`, - Use: "set [file|auto]", - Short: "Used to configure the vault backends", - DisableFlagsInUseLine: true, - Args: cobra.MinimumNArgs(1), - Run: func(cmd *cobra.Command, args []string) { - wantedVaultTypeName := args[0] - currentVaultBackend, err := util.GetCurrentVaultBackend() - if err != nil { - log.Error().Msgf("Unable to set vault to [%s] because of [err=%s]", wantedVaultTypeName, err) - return - } - - if wantedVaultTypeName == string(currentVaultBackend) { - log.Error().Msgf("You are already on vault backend [%s]", currentVaultBackend) - return - } - - if wantedVaultTypeName == util.VAULT_BACKEND_AUTO_MODE || wantedVaultTypeName == util.VAULT_BACKEND_FILE_MODE { - configFile, err := util.GetConfigFile() - if err != nil { - log.Error().Msgf("Unable to set vault to [%s] because of [err=%s]", wantedVaultTypeName, err) - return - } - - configFile.VaultBackendType = wantedVaultTypeName - configFile.LoggedInUserEmail = "" - configFile.VaultBackendPassphrase = base64.StdEncoding.EncodeToString([]byte(util.GenerateRandomString(10))) - - err = util.WriteConfigFile(&configFile) - if err != nil { - log.Error().Msgf("Unable to set vault to [%s] because an error occurred when saving the config file [err=%s]", wantedVaultTypeName, err) - return - } - - 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]\n", currentVaultBackend, wantedVaultTypeName) - - Telemetry.CaptureEvent("cli-command:vault set", posthog.NewProperties().Set("currentVault", currentVaultBackend).Set("wantedVault", wantedVaultTypeName).Set("version", util.CLI_VERSION)) - } else { - var availableVaultsNames []string - for _, vault := range AvailableVaults { - availableVaultsNames = append(availableVaultsNames, vault.Name) - } - 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, strings.Join(availableVaultsNames, ", ")) - } - }, -} - -// runCmd represents the run command -var vaultCmd = &cobra.Command{ - Use: "vault", - Short: "Used to manage where your Infisical login token is saved on your machine", - DisableFlagsInUseLine: true, - Args: cobra.NoArgs, - Run: func(cmd *cobra.Command, args []string) { - printAvailableVaultBackends() - }, -} - -func printAvailableVaultBackends() { - fmt.Printf("Vaults are used to securely store your login details locally. Available vaults:") - for _, vaultType := range AvailableVaults { - fmt.Printf("\n- %s (%s)", vaultType.Name, vaultType.Description) - } - - currentVaultBackend, err := util.GetCurrentVaultBackend() - if err != nil { - 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\n", string(currentVaultBackend)) -} - -func init() { - vaultCmd.AddCommand(vaultSetCmd) - - rootCmd.AddCommand(vaultCmd) -} diff --git a/cli/packages/config/config.go b/cli/packages/config/config.go deleted file mode 100644 index c5e162c92..000000000 --- a/cli/packages/config/config.go +++ /dev/null @@ -1,5 +0,0 @@ -package config - -var INFISICAL_URL string -var INFISICAL_URL_MANUAL_OVERRIDE string -var INFISICAL_LOGIN_URL string diff --git a/cli/packages/crypto/crypto.go b/cli/packages/crypto/crypto.go deleted file mode 100644 index 2f507ed4c..000000000 --- a/cli/packages/crypto/crypto.go +++ /dev/null @@ -1,86 +0,0 @@ -package crypto - -import ( - "crypto/aes" - "crypto/cipher" - "crypto/rand" - "io" - - "github.com/Infisical/infisical-merge/packages/models" - "golang.org/x/crypto/nacl/box" -) - -// will decrypt cipher text to plain text using iv and tag -func DecryptSymmetric(key []byte, cipherText []byte, tag []byte, iv []byte) ([]byte, error) { - // Case: empty string - if len(cipherText) == 0 && len(tag) == 0 && len(iv) == 0 { - return []byte{}, nil - } - - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - aesgcm, err := cipher.NewGCMWithNonceSize(block, len(iv)) - if err != nil { - return nil, err - } - - var nonce = iv - var ciphertext = append(cipherText, tag...) // the aesgcm open method expects auth tag at the end of the cipher text - - plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil) - if err != nil { - return nil, err - } - - return plaintext, nil -} - -func GenerateNewKey() (newKey []byte, keyErr error) { - key := make([]byte, 16) // block size defaults to 16 so this is fine - _, err := rand.Read(key) - return key, err -} - -// Will encrypt a plain text with the provided key -func EncryptSymmetric(plaintext []byte, key []byte) (result models.SymmetricEncryptionResult, err error) { - block, err := aes.NewCipher(key) - if err != nil { - return models.SymmetricEncryptionResult{}, err - } - - aesgcm, err := cipher.NewGCMWithNonceSize(block, 16) // default is 12, 16 because https://github.com/Infisical/infisical/blob/bea0ff6e05a4de73a5db625d4ae181a015b50855/backend/src/utils/aes-gcm.ts#L4 - if err != nil { - return models.SymmetricEncryptionResult{}, err - } - - // create a nonce - nonce := make([]byte, aesgcm.NonceSize()) - if _, err := io.ReadFull(rand.Reader, nonce); err != nil { - panic(err) - } - - ciphertext := aesgcm.Seal(nil, nonce, plaintext, nil) - - ciphertextOnly := ciphertext[:len(ciphertext)-16] // combines the auth tag with the cipher text so we need to extract it - - authTag := ciphertext[len(ciphertext)-16:] - - return models.SymmetricEncryptionResult{ - CipherText: ciphertextOnly, - AuthTag: authTag, - Nonce: nonce, - }, nil -} - -func DecryptAsymmetric(ciphertext []byte, nonce []byte, publicKey []byte, privateKey []byte) (plainText []byte) { - plainTextToReturn, _ := box.Open(nil, ciphertext, (*[24]byte)(nonce), (*[32]byte)(publicKey), (*[32]byte)(privateKey)) - return plainTextToReturn -} - -func EncryptAssymmetric(message []byte, nonce []byte, publicKey []byte, privateKey []byte) (encryptedMessage []byte) { - encryptedPlainText := box.Seal(nil, message, (*[24]byte)(nonce), (*[32]byte)(publicKey), (*[32]byte)(privateKey)) - return encryptedPlainText -} diff --git a/cli/packages/gateway/connection.go b/cli/packages/gateway/connection.go deleted file mode 100644 index 980137374..000000000 --- a/cli/packages/gateway/connection.go +++ /dev/null @@ -1,358 +0,0 @@ -package gateway - -import ( - "bufio" - "bytes" - "context" - "crypto/tls" - "crypto/x509" - "encoding/base64" - "errors" - "fmt" - "io" - "net" - "net/http" - "net/url" - "os" - "strings" - "sync" - "time" - - "github.com/quic-go/quic-go" - "github.com/rs/zerolog/log" -) - -func handleConnection(ctx context.Context, quicConn quic.Connection) { - log.Info().Msgf("New connection from: %s", quicConn.RemoteAddr().String()) - // Use WaitGroup to track all streams - var wg sync.WaitGroup - - contextWithTimeout, cancel := context.WithTimeout(ctx, 30*time.Second) - defer cancel() - - for { - // Accept the first stream, which we'll use for commands - stream, err := quicConn.AcceptStream(contextWithTimeout) - if err != nil { - log.Printf("Failed to accept QUIC stream: %v", err) - break - } - wg.Add(1) - go func(stream quic.Stream) { - defer wg.Done() - defer stream.Close() - - handleStream(stream, quicConn) - }(stream) - } - - wg.Wait() - log.Printf("All streams closed for connection: %s", quicConn.RemoteAddr().String()) -} - -func handleStream(stream quic.Stream, quicConn quic.Connection) { - streamID := stream.StreamID() - log.Printf("New stream %d from: %s", streamID, quicConn.RemoteAddr().String()) - - // Use buffered reader for better handling of fragmented data - reader := bufio.NewReader(stream) - defer func() { - log.Info().Msgf("Closing stream %d", streamID) - if stream != nil { - stream.Close() - } - }() - - for { - msg, err := reader.ReadBytes('\n') - if err != nil { - if errors.Is(err, io.EOF) { - return - } - log.Error().Msgf("Error reading command: %s", err) - return - } - - cmd := bytes.ToUpper(bytes.TrimSpace(bytes.Split(msg, []byte(" "))[0])) - args := bytes.TrimSpace(bytes.TrimPrefix(msg, cmd)) - - switch string(cmd) { - case "FORWARD-TCP": - proxyAddress := string(bytes.Split(args, []byte(" "))[0]) - destTarget, err := net.Dial("tcp", proxyAddress) - if err != nil { - log.Error().Msgf("Failed to connect to target: %v", err) - return - } - defer destTarget.Close() - log.Info().Msgf("Starting secure transmission between %s->%s", quicConn.LocalAddr().String(), destTarget.LocalAddr().String()) - - // Handle buffered data - buffered := reader.Buffered() - if buffered > 0 { - bufferedData := make([]byte, buffered) - _, err := reader.Read(bufferedData) - if err != nil { - log.Error().Msgf("Error reading buffered data: %v", err) - return - } - - if _, err = destTarget.Write(bufferedData); err != nil { - log.Error().Msgf("Error writing buffered data: %v", err) - return - } - } - - CopyDataFromQuicToTcp(stream, destTarget) - log.Info().Msgf("Ending secure transmission between %s->%s", quicConn.LocalAddr().String(), destTarget.LocalAddr().String()) - return - - case "FORWARD-HTTP": - targetURL := "" - argParts := bytes.Split(args, []byte(" ")) - - if len(argParts) == 0 || len(argParts[0]) == 0 { - log.Warn().Msg("FORWARD-HTTP used without a target URL.") - } else { - targetURL = string(argParts[0]) - if !isValidURL(targetURL) { - log.Error().Msgf("Invalid target URL: %s", targetURL) - return - } - } - - // Parse optional parameters - var caCertB64, verifyParam string - for _, part := range argParts[1:] { - partStr := string(part) - if strings.HasPrefix(partStr, "ca=") { - caCertB64 = strings.TrimPrefix(partStr, "ca=") - } else if strings.HasPrefix(partStr, "verify=") { - verifyParam = strings.TrimPrefix(partStr, "verify=") - } - } - - log.Info().Msgf("Starting HTTP proxy to: %s", targetURL) - - if err := handleHTTPProxy(stream, reader, targetURL, caCertB64, verifyParam); err != nil { - log.Error().Msgf("HTTP proxy error: %v", err) - } - return - - case "PING": - if _, err := stream.Write([]byte("PONG\n")); err != nil { - log.Error().Msgf("Error writing PONG response: %v", err) - } - return - default: - log.Error().Msgf("Unknown command: %s", string(cmd)) - return - } - } -} -func handleHTTPProxy(stream quic.Stream, reader *bufio.Reader, targetURL string, caCertB64 string, verifyParam string) error { - transport := &http.Transport{ - DisableKeepAlives: false, - MaxIdleConns: 10, - IdleConnTimeout: 30 * time.Second, - } - - if strings.HasPrefix(targetURL, "https://") { - tlsConfig := &tls.Config{} - - if caCertB64 != "" { - caCert, err := base64.StdEncoding.DecodeString(caCertB64) - if err == nil { - caCertPool := x509.NewCertPool() - if caCertPool.AppendCertsFromPEM(caCert) { - tlsConfig.RootCAs = caCertPool - log.Info().Msg("Using provided CA certificate from gateway client") - } else { - log.Error().Msg("Failed to parse provided CA certificate") - } - } else { - log.Error().Msgf("Failed to decode CA certificate: %v", err) - } - } - - if verifyParam != "" { - tlsConfig.InsecureSkipVerify = verifyParam == "false" - log.Info().Msgf("TLS verification set to: %s", verifyParam) - } - - transport.TLSClientConfig = tlsConfig - } - - // Loop to handle multiple HTTP requests on the same stream - for { - req, err := http.ReadRequest(reader) - - if err != nil { - if errors.Is(err, io.EOF) { - log.Info().Msg("Client closed HTTP connection") - return nil - } - return fmt.Errorf("failed to read HTTP request: %v", err) - } - log.Info().Msgf("Received HTTP request: %s", req.URL.Path) - - actionHeader := HttpProxyAction(req.Header.Get(INFISICAL_HTTP_PROXY_ACTION_HEADER)) - if actionHeader != "" { - if actionHeader == HttpProxyActionInjectGatewayK8sServiceAccountToken { - token, err := os.ReadFile(KUBERNETES_SERVICE_ACCOUNT_TOKEN_PATH) - if err != nil { - stream.Write([]byte(buildHttpInternalServerError("failed to read k8s sa auth token"))) - continue // Continue to next request instead of returning - } - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", string(token))) - log.Info().Msgf("Injected gateway k8s SA auth token in request to %s", targetURL) - } else if actionHeader == HttpProxyActionUseGatewayK8sServiceAccount { // will work without a target URL set - // set the ca cert to the pod's k8s service account ca cert: - caCert, err := os.ReadFile(KUBERNETES_SERVICE_ACCOUNT_CA_CERT_PATH) - if err != nil { - stream.Write([]byte(buildHttpInternalServerError("failed to read k8s sa ca cert"))) - continue - } - - caCertPool := x509.NewCertPool() - if ok := caCertPool.AppendCertsFromPEM(caCert); !ok { - stream.Write([]byte(buildHttpInternalServerError("failed to parse k8s sa ca cert"))) - continue - } - - transport.TLSClientConfig = &tls.Config{ - RootCAs: caCertPool, - } - - // set authorization header to the pod's k8s service account token: - token, err := os.ReadFile(KUBERNETES_SERVICE_ACCOUNT_TOKEN_PATH) - if err != nil { - stream.Write([]byte(buildHttpInternalServerError("failed to read k8s sa auth token"))) - continue - } - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", string(token))) - - // update the target URL to point to the kubernetes API server: - kubernetesServiceHost := os.Getenv(KUBERNETES_SERVICE_HOST_ENV_NAME) - kubernetesServicePort := os.Getenv(KUBERNETES_SERVICE_PORT_HTTPS_ENV_NAME) - - fullBaseUrl := fmt.Sprintf("https://%s:%s", kubernetesServiceHost, kubernetesServicePort) - targetURL = fullBaseUrl - - log.Info().Msgf("Redirected request to Kubernetes API server: %s", targetURL) - } - - req.Header.Del(INFISICAL_HTTP_PROXY_ACTION_HEADER) - } - - // Build full target URL - var targetFullURL string - if strings.HasPrefix(targetURL, "http://") || strings.HasPrefix(targetURL, "https://") { - baseURL := strings.TrimSuffix(targetURL, "/") - targetFullURL = baseURL + req.URL.Path - if req.URL.RawQuery != "" { - targetFullURL += "?" + req.URL.RawQuery - } - } else { - baseURL := strings.TrimSuffix("http://"+targetURL, "/") - targetFullURL = baseURL + req.URL.Path - if req.URL.RawQuery != "" { - targetFullURL += "?" + req.URL.RawQuery - } - } - - // create the request to the target - proxyReq, err := http.NewRequest(req.Method, targetFullURL, req.Body) - if err != nil { - log.Error().Msgf("Failed to create proxy request: %v", err) - stream.Write([]byte(buildHttpInternalServerError("failed to create proxy request"))) - continue // Continue to next request - } - proxyReq.Header = req.Header.Clone() - - log.Info().Msgf("Proxying %s %s to %s", req.Method, req.URL.Path, targetFullURL) - - client := &http.Client{ - Transport: transport, - Timeout: 30 * time.Second, - } - - resp, err := client.Do(proxyReq) - if err != nil { - log.Error().Msgf("Failed to reach target: %v", err) - stream.Write([]byte(buildHttpInternalServerError(fmt.Sprintf("failed to reach target due to networking error: %s", err.Error())))) - continue // Continue to next request - } - - // Write the entire response (status line, headers, body) to the stream - // http.Response.Write handles this for "Connection: close" correctly. - // For other connection tokens, manual removal might be needed if they cause issues with QUIC. - // For a simple proxy, this is generally sufficient. - resp.Header.Del("Connection") // Good practice for proxies - - log.Info().Msgf("Writing response to stream: %s", resp.Status) - - if err := resp.Write(stream); err != nil { - log.Error().Err(err).Msg("Failed to write response to stream") - resp.Body.Close() - return fmt.Errorf("failed to write response to stream: %w", err) - } - - resp.Body.Close() - - // Check if client wants to close connection - if req.Header.Get("Connection") == "close" { - log.Info().Msg("Client requested connection close") - return nil - } - } -} - -func buildHttpInternalServerError(message string) string { - return fmt.Sprintf("HTTP/1.1 500 Internal Server Error\r\nContent-Type: application/json\r\n\r\n{\"message\": \"gateway: %s\"}", message) -} - -type CloseWrite interface { - CloseWrite() error -} - -func isValidURL(str string) bool { - u, err := url.Parse(str) - return err == nil && u.Scheme != "" && u.Host != "" -} - -func CopyDataFromQuicToTcp(quicStream quic.Stream, tcpConn net.Conn) { - // Create a WaitGroup to wait for both copy operations - var wg sync.WaitGroup - wg.Add(2) - - // Start copying from QUIC stream to TCP - go func() { - defer wg.Done() - if _, err := io.Copy(tcpConn, quicStream); err != nil { - log.Error().Msgf("Error copying quic->postgres: %v", err) - } - - if e, ok := tcpConn.(CloseWrite); ok { - log.Debug().Msg("Closing TCP write end") - e.CloseWrite() - } else { - log.Debug().Msg("TCP connection does not support CloseWrite") - } - }() - - // Start copying from TCP to QUIC stream - go func() { - defer wg.Done() - if _, err := io.Copy(quicStream, tcpConn); err != nil { - log.Debug().Msgf("Error copying postgres->quic: %v", err) - } - // Close the write side of the QUIC stream - if err := quicStream.Close(); err != nil && !strings.Contains(err.Error(), "close called for canceled stream") { - log.Error().Msgf("Error closing QUIC stream write: %v", err) - } - }() - - // Wait for both copies to complete - wg.Wait() -} diff --git a/cli/packages/gateway/constants.go b/cli/packages/gateway/constants.go deleted file mode 100644 index aa260ed2e..000000000 --- a/cli/packages/gateway/constants.go +++ /dev/null @@ -1,17 +0,0 @@ -package gateway - -const ( - KUBERNETES_SERVICE_HOST_ENV_NAME = "KUBERNETES_SERVICE_HOST" - KUBERNETES_SERVICE_PORT_HTTPS_ENV_NAME = "KUBERNETES_SERVICE_PORT_HTTPS" - KUBERNETES_SERVICE_ACCOUNT_CA_CERT_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" - KUBERNETES_SERVICE_ACCOUNT_TOKEN_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/token" - - INFISICAL_HTTP_PROXY_ACTION_HEADER = "x-infisical-action" -) - -type HttpProxyAction string - -const ( - HttpProxyActionInjectGatewayK8sServiceAccountToken HttpProxyAction = "inject-k8s-sa-auth-token" - HttpProxyActionUseGatewayK8sServiceAccount HttpProxyAction = "use-k8s-sa" -) diff --git a/cli/packages/gateway/gateway.go b/cli/packages/gateway/gateway.go deleted file mode 100644 index eb0c72d5d..000000000 --- a/cli/packages/gateway/gateway.go +++ /dev/null @@ -1,371 +0,0 @@ -package gateway - -import ( - "context" - "crypto/tls" - "crypto/x509" - "fmt" - "net" - "os" - "strings" - "sync" - "time" - - "github.com/Infisical/infisical-merge/packages/api" - "github.com/Infisical/infisical-merge/packages/systemd" - "github.com/Infisical/infisical-merge/packages/util" - "github.com/go-resty/resty/v2" - "github.com/pion/dtls/v3" - "github.com/pion/logging" - "github.com/pion/turn/v4" - "github.com/rs/zerolog/log" - - "github.com/quic-go/quic-go" -) - -type GatewayConfig struct { - TurnServerUsername string - TurnServerPassword string - TurnServerAddress string - InfisicalStaticIp string - SerialNumber string - PrivateKey string - Certificate string - CertificateChain string -} - -type Gateway struct { - httpClient *resty.Client - config *GatewayConfig - client *turn.Client -} - -func NewGateway(identityToken string) (Gateway, error) { - httpClient, err := util.GetRestyClientWithCustomHeaders() - if err != nil { - return Gateway{}, fmt.Errorf("unable to get client with custom headers [err=%v]", err) - } - - httpClient.SetAuthToken(identityToken) - - return Gateway{ - httpClient: httpClient, - config: &GatewayConfig{}, - }, nil -} - -func (g *Gateway) UpdateIdentityAccessToken(accessToken string) { - g.httpClient.SetAuthToken(accessToken) -} - -func (g *Gateway) ConnectWithRelay() error { - relayDetails, err := api.CallRegisterGatewayIdentityV1(g.httpClient) - if err != nil { - return err - } - relayAddress, relayPort := strings.Split(relayDetails.TurnServerAddress, ":")[0], strings.Split(relayDetails.TurnServerAddress, ":")[1] - - // Start a new TURN Client and wrap our net.Conn in a STUNConn - // This allows us to simulate datagram based communication over a net.Conn - logger := logging.NewDefaultLoggerFactory() - if os.Getenv("LOG_LEVEL") == "debug" { - logger.DefaultLogLevel = logging.LogLevelDebug - } - - turnClientCfg := &turn.ClientConfig{ - STUNServerAddr: relayDetails.TurnServerAddress, - TURNServerAddr: relayDetails.TurnServerAddress, - Username: relayDetails.TurnServerUsername, - Password: relayDetails.TurnServerPassword, - Realm: relayDetails.TurnServerRealm, - LoggerFactory: logger, - } - - turnAddr, err := net.ResolveUDPAddr("udp4", relayDetails.TurnServerAddress) - if err != nil { - return fmt.Errorf("Failed to parse turn server address: %w", err) - } - - // Dial TURN Server - if relayPort == "5349" { - log.Info().Msgf("Provided relay port %s. Using TLS", relayPort) - conn, err := dtls.Dial("udp", turnAddr, &dtls.Config{ - ServerName: relayAddress, - }) - if err != nil { - return fmt.Errorf("Failed to connect with relay server: %w", err) - } - turnClientCfg.Conn = turn.NewSTUNConn(conn) - } else { - log.Info().Msgf("Provided relay port %s. Using non TLS connection.", relayPort) - conn, err := net.ListenPacket("udp4", "0.0.0.0:0") - if err != nil { - return fmt.Errorf("Failed to connect with relay server: %w", err) - } - - turnClientCfg.Conn = conn - } - - client, err := turn.NewClient(turnClientCfg) - if err != nil { - return fmt.Errorf("Failed to create relay client: %w", err) - } - - g.config = &GatewayConfig{ - TurnServerUsername: relayDetails.TurnServerUsername, - TurnServerPassword: relayDetails.TurnServerPassword, - TurnServerAddress: relayDetails.TurnServerAddress, - InfisicalStaticIp: relayDetails.InfisicalStaticIp, - } - - g.client = client - return nil -} - -func (g *Gateway) Listen(ctx context.Context) error { - defer g.client.Close() - err := g.client.Listen() - if err != nil { - return fmt.Errorf("Failed to listen to relay server: %w", err) - } - - log.Info().Msg("Connected with relay") - - // Allocate a relay socket on the TURN server. On success, it - // will return a net.PacketConn which represents the remote - // socket. - relayUdpConnection, err := g.client.Allocate() - if err != nil { - return fmt.Errorf("Failed to allocate relay connection: %w", err) - } - - log.Info().Msg(relayUdpConnection.LocalAddr().String()) - defer func() { - if closeErr := relayUdpConnection.Close(); closeErr != nil { - log.Error().Msgf("Failed to close connection: %s", closeErr) - } - }() - - gatewayCert, err := api.CallExchangeRelayCertV1(g.httpClient, api.ExchangeRelayCertRequestV1{ - RelayAddress: relayUdpConnection.LocalAddr().String(), - }) - if err != nil { - return err - } - - g.config.SerialNumber = gatewayCert.SerialNumber - g.config.PrivateKey = gatewayCert.PrivateKey - g.config.Certificate = gatewayCert.Certificate - g.config.CertificateChain = gatewayCert.CertificateChain - - errCh := make(chan error, 1) - shutdownCh := make(chan bool, 1) - - if err = g.createPermissionForStaticIps(g.config.InfisicalStaticIp); err != nil { - return err - } - - g.registerHeartBeat(ctx, errCh) - - cert, err := tls.X509KeyPair([]byte(gatewayCert.Certificate), []byte(gatewayCert.PrivateKey)) - if err != nil { - return fmt.Errorf("failed to parse cert: %w", err) - } - - caCertPool := x509.NewCertPool() - caCertPool.AppendCertsFromPEM([]byte(gatewayCert.CertificateChain)) - - // Setup QUIC server - tlsConfig := &tls.Config{ - Certificates: []tls.Certificate{cert}, - MinVersion: tls.VersionTLS12, - ClientCAs: caCertPool, - ClientAuth: tls.RequireAndVerifyClientCert, - NextProtos: []string{"infisical-gateway"}, - } - // Setup QUIC listener on the relayConn - quicConfig := &quic.Config{ - EnableDatagrams: true, - MaxIdleTimeout: 10 * time.Second, - KeepAlivePeriod: 2 * time.Second, - } - - quicListener, err := quic.Listen(relayUdpConnection, tlsConfig, quicConfig) - if err != nil { - return fmt.Errorf("Failed to listen for QUIC: %w", err) - } - defer quicListener.Close() - - log.Printf("Listener started on %s", quicListener.Addr()) - - g.registerRelayIsActive(ctx, errCh) - - log.Info().Msg("Gateway started successfully") - - var wg sync.WaitGroup - - go func() { - for { - select { - case <-ctx.Done(): - return - case <-shutdownCh: - return - default: - // Accept new relay connection - quicConn, err := quicListener.Accept(context.Background()) - if err != nil { - log.Printf("Failed to accept QUIC connection: %v", err) - continue - } - - tlsState := quicConn.ConnectionState().TLS - if len(tlsState.PeerCertificates) > 0 { - organizationUnit := tlsState.PeerCertificates[0].Subject.OrganizationalUnit - commonName := tlsState.PeerCertificates[0].Subject.CommonName - if organizationUnit[0] != "gateway-client" || commonName != "cloud" { - errMsg := fmt.Sprintf("Client certificate verification failed. Received %s, %s", organizationUnit, commonName) - log.Error().Msg(errMsg) - quicConn.CloseWithError(1, errMsg) - continue - } - } - - // Handle the connection in a goroutine - wg.Add(1) - go func(c quic.Connection) { - defer wg.Done() - defer c.CloseWithError(0, "connection closed") - - // Monitor parent context to close this connection when needed - go func() { - select { - case <-ctx.Done(): - c.CloseWithError(0, "connection closed") // Force close connection when context is canceled - case <-shutdownCh: - c.CloseWithError(0, "connection closed") // Force close connection when accepting loop is done - } - }() - - handleConnection(ctx, c) - }(quicConn) - } - } - }() - - // make this compatiable with systemd notify mode - systemd.SdNotify(false, systemd.SdNotifyReady) - select { - case <-ctx.Done(): - log.Info().Msg("Shutting down gateway...") - case err = <-errCh: - log.Error().Err(err).Msg("Gateway error occurred") - } - - // Signal the accept loop to stop - close(shutdownCh) - - // Set a timeout for waiting on connections to close - waitCh := make(chan struct{}) - go func() { - wg.Wait() - close(waitCh) - }() - - select { - case <-waitCh: - // All connections closed normally - case <-time.After(5 * time.Second): - log.Warn().Msg("Timeout waiting for connections to close gracefully") - } - - return err -} - -func (g *Gateway) registerHeartBeat(ctx context.Context, errCh chan error) { - ticker := time.NewTicker(30 * time.Minute) - defer ticker.Stop() - - go func() { - for { - if err := api.CallGatewayHeartBeatV1(g.httpClient); err != nil { - errCh <- err - } else { - log.Info().Msg("Gateway is reachable by Infisical") - } - - select { - case <-ctx.Done(): - return - case <-ticker.C: - } - } - }() -} - -func (g *Gateway) createPermissionForStaticIps(staticIps string) error { - if staticIps == "" { - return fmt.Errorf("Missing Infisical static ips for permission") - } - - splittedIps := strings.Split(staticIps, ",") - resolvedIps := make([]net.Addr, 0) - for _, ip := range splittedIps { - ip = strings.TrimSpace(ip) - if ip == "" { - continue - } - - // if port not specific allow all port - if !strings.Contains(ip, ":") { - ip = ip + ":0" - } - - peerAddr, err := net.ResolveUDPAddr("udp", ip) - if err != nil { - return fmt.Errorf("Failed to resolve static ip for permission: %w", err) - } - - resolvedIps = append(resolvedIps, peerAddr) - } - - if err := g.client.CreatePermission(resolvedIps...); err != nil { - return fmt.Errorf("Failed to set ip permission: %w", err) - } - return nil -} - -func (g *Gateway) registerRelayIsActive(ctx context.Context, errCh chan error) error { - ticker := time.NewTicker(15 * time.Second) - maxFailures := 3 - failures := 0 - - log.Info().Msg("Starting relay connection health check") - go func() { - time.Sleep(5 * time.Second) - for { - select { - case <-ctx.Done(): - log.Info().Msg("Stopping relay connection health check") - return - case <-ticker.C: - log.Debug().Msg("Performing relay connection health check") - err := g.createPermissionForStaticIps(g.config.InfisicalStaticIp) - // try again error message from server happens to avoid congestion - // https://github.com/pion/turn/blob/master/internal/client/udp_conn.go#L382 - if err != nil && !strings.Contains(err.Error(), "try again") { - failures++ - log.Warn().Err(err).Int("failures", failures).Msg("Failed to refresh TURN permissions") - if failures >= maxFailures { - errCh <- fmt.Errorf("relay connection check failed: %w", err) - return - } - continue - } - failures = 0 // reset - } - } - }() - - return nil -} diff --git a/cli/packages/gateway/relay.go b/cli/packages/gateway/relay.go deleted file mode 100644 index 08a5eb247..000000000 --- a/cli/packages/gateway/relay.go +++ /dev/null @@ -1,188 +0,0 @@ -//go:build !windows -// +build !windows - -package gateway - -import ( - "crypto/tls" - "crypto/x509" - "errors" - "fmt" - "net" - "os" - "os/signal" - - // "runtime" - "strconv" - "syscall" - - "github.com/Infisical/infisical-merge/packages/systemd" - "github.com/pion/dtls/v3" - "github.com/pion/logging" - "github.com/pion/turn/v4" - "github.com/rs/zerolog/log" - "gopkg.in/yaml.v2" -) - -var ( - errMissingTlsCert = errors.New("Missing TLS files") -) - -type GatewayRelay struct { - Config *GatewayRelayConfig -} - -type GatewayRelayConfig struct { - PublicIP string `yaml:"public_ip"` - Port int `yaml:"port"` - Realm string `yaml:"realm"` - AuthSecret string `yaml:"auth_secret"` - RelayMinPort uint16 `yaml:"relay_min_port"` - RelayMaxPort uint16 `yaml:"relay_max_port"` - TlsCertPath string `yaml:"tls_cert_path"` - TlsPrivateKeyPath string `yaml:"tls_private_key_path"` - TlsCaPath string `yaml:"tls_ca_path"` - - tls tls.Certificate - tlsCa string - isTlsEnabled bool -} - -func NewGatewayRelay(configFilePath string) (*GatewayRelay, error) { - cfgFile, err := os.ReadFile(configFilePath) - if err != nil { - return nil, err - } - var cfg GatewayRelayConfig - if err := yaml.Unmarshal(cfgFile, &cfg); err != nil { - return nil, err - } - - if cfg.PublicIP == "" { - return nil, fmt.Errorf("Missing public ip") - } - - if cfg.AuthSecret == "" { - return nil, fmt.Errorf("Missing auth secret") - } - - if cfg.Realm == "" { - cfg.Realm = "infisical.org" - } - - if cfg.RelayMinPort == 0 { - cfg.RelayMinPort = 49152 - } - - if cfg.RelayMaxPort == 0 { - cfg.RelayMaxPort = 65535 - } - - if cfg.Port == 0 { - cfg.Port = 3478 - } else if cfg.Port == 5349 { - if cfg.TlsCertPath == "" || cfg.TlsPrivateKeyPath == "" { - return nil, errMissingTlsCert - } - - cert, err := tls.LoadX509KeyPair(cfg.TlsCertPath, cfg.TlsPrivateKeyPath) - if err != nil { - return nil, fmt.Errorf("Failed to read load server tls key pair: %w", err) - } - - if cfg.TlsCaPath != "" { - ca, err := os.ReadFile(cfg.TlsCaPath) - if err != nil { - return nil, fmt.Errorf("Failed to read tls ca: %w", err) - } - cfg.tlsCa = string(ca) - } - - cfg.tls = cert - cfg.isTlsEnabled = true - } - - return &GatewayRelay{ - Config: &cfg, - }, nil -} - -func (g *GatewayRelay) Run() error { - addr, err := net.ResolveUDPAddr("udp", "0.0.0.0:"+strconv.Itoa(g.Config.Port)) - if err != nil { - return fmt.Errorf("Failed to parse server address: %s", err) - } - - // NewLongTermAuthHandler takes a pion.LeveledLogger. This allows you to intercept messages - // and process them yourself. - logger := logging.NewDefaultLeveledLoggerForScope("lt-creds", logging.LogLevelTrace, os.Stdout) - - publicIP := g.Config.PublicIP - relayAddressGenerator := &turn.RelayAddressGeneratorPortRange{ - RelayAddress: net.ParseIP(publicIP), // Claim that we are listening on IP passed by user - Address: "0.0.0.0", // But actually be listening on every interface - MinPort: g.Config.RelayMinPort, - MaxPort: g.Config.RelayMaxPort, - } - - loggerF := logging.NewDefaultLoggerFactory() - loggerF.DefaultLogLevel = logging.LogLevelDebug - - caCertPool := x509.NewCertPool() - caCertPool.AppendCertsFromPEM([]byte(g.Config.tlsCa)) - - listenerConfigs := make([]turn.ListenerConfig, 0) - packetConfigs := make([]turn.PacketConnConfig, 0) - - if g.Config.isTlsEnabled { - caCertPool := x509.NewCertPool() - caCertPool.AppendCertsFromPEM([]byte(g.Config.tlsCa)) - dtlsServer, err := dtls.Listen("udp", addr, &dtls.Config{ - Certificates: []tls.Certificate{g.Config.tls}, - ClientCAs: caCertPool, - }) - if err != nil { - return fmt.Errorf("Failed to start dtls server: %w", err) - } - listenerConfigs = append(listenerConfigs, turn.ListenerConfig{ - RelayAddressGenerator: relayAddressGenerator, - Listener: dtlsServer, - }) - } else { - udpListener, err := net.ListenPacket("udp4", "0.0.0.0:"+strconv.Itoa(g.Config.Port)) - if err != nil { - return fmt.Errorf("Failed to relay udp listener: %w", err) - } - packetConfigs = append(packetConfigs, turn.PacketConnConfig{ - RelayAddressGenerator: relayAddressGenerator, - PacketConn: udpListener, - }) - } - - server, err := turn.NewServer(turn.ServerConfig{ - Realm: g.Config.Realm, - AuthHandler: turn.LongTermTURNRESTAuthHandler(g.Config.AuthSecret, logger), - // PacketConnConfigs is a list of UDP Listeners and the configuration around them - ListenerConfigs: listenerConfigs, - PacketConnConfigs: packetConfigs, - LoggerFactory: loggerF, - }) - - if err != nil { - return fmt.Errorf("Failed to start server: %w", err) - } - - log.Info().Msgf("Relay listening on %d\n", g.Config.Port) - - // make this compatiable with systemd notify mode - systemd.SdNotify(false, systemd.SdNotifyReady) - // Block until user sends SIGINT or SIGTERM - sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) - <-sigs - - if err = server.Close(); err != nil { - return fmt.Errorf("Failed to close server: %w", err) - } - return nil -} diff --git a/cli/packages/gateway/relay_windows.go b/cli/packages/gateway/relay_windows.go deleted file mode 100644 index f3bf89bd0..000000000 --- a/cli/packages/gateway/relay_windows.go +++ /dev/null @@ -1,37 +0,0 @@ -//go:build windows -// +build windows - -package gateway - -import ( - "errors" -) - -var ( - errMissingTlsCert = errors.New("Missing TLS files") - errWindowsNotSupported = errors.New("Relay is not supported on Windows") -) - -type GatewayRelay struct { - Config *GatewayRelayConfig -} - -type GatewayRelayConfig struct { - PublicIP string - Port int - Realm string - AuthSecret string - RelayMinPort uint16 - RelayMaxPort uint16 - TlsCertPath string - TlsPrivateKeyPath string - TlsCaPath string -} - -func NewGatewayRelay(configFilePath string) (*GatewayRelay, error) { - return nil, errWindowsNotSupported -} - -func (g *GatewayRelay) Run() error { - return errWindowsNotSupported -} diff --git a/cli/packages/gateway/systemd.go b/cli/packages/gateway/systemd.go deleted file mode 100644 index ac6663dff..000000000 --- a/cli/packages/gateway/systemd.go +++ /dev/null @@ -1,121 +0,0 @@ -package gateway - -import ( - "fmt" - "os" - "os/exec" - "path/filepath" - "runtime" - - "github.com/rs/zerolog/log" -) - -const systemdServiceTemplate = `[Unit] -Description=Infisical Gateway Service -After=network.target - -[Service] -Type=notify -NotifyAccess=all -EnvironmentFile=/etc/infisical/gateway.conf -ExecStart=infisical gateway -Restart=on-failure -InaccessibleDirectories=/home -PrivateTmp=yes -LimitCORE=infinity -LimitNOFILE=1000000 -LimitNPROC=60000 -LimitRTPRIO=infinity -LimitRTTIME=7000000 - -[Install] -WantedBy=multi-user.target -` - -func InstallGatewaySystemdService(token string, domain string) error { - if runtime.GOOS != "linux" { - log.Info().Msg("Skipping systemd service installation - not on Linux") - return nil - } - - if os.Geteuid() != 0 { - log.Info().Msg("Skipping systemd service installation - not running as root/sudo") - return nil - } - - configDir := "/etc/infisical" - if err := os.MkdirAll(configDir, 0755); err != nil { - return fmt.Errorf("failed to create config directory: %v", err) - } - - configContent := fmt.Sprintf("INFISICAL_UNIVERSAL_AUTH_ACCESS_TOKEN=%s\n", token) - if domain != "" { - configContent += fmt.Sprintf("INFISICAL_API_URL=%s\n", domain) - } - - configPath := filepath.Join(configDir, "gateway.conf") - if err := os.WriteFile(configPath, []byte(configContent), 0600); err != nil { - return fmt.Errorf("failed to write config file: %v", err) - } - - servicePath := "/etc/systemd/system/infisical-gateway.service" - if err := os.WriteFile(servicePath, []byte(systemdServiceTemplate), 0644); err != nil { - return fmt.Errorf("failed to write systemd service file: %v", err) - } - - reloadCmd := exec.Command("systemctl", "daemon-reload") - if err := reloadCmd.Run(); err != nil { - return fmt.Errorf("failed to reload systemd: %v", err) - } - - log.Info().Msg("Successfully installed systemd service") - log.Info().Msg("To start the service, run: sudo systemctl start infisical-gateway") - log.Info().Msg("To enable the service on boot, run: sudo systemctl enable infisical-gateway") - - return nil -} - -func UninstallGatewaySystemdService() error { - if runtime.GOOS != "linux" { - log.Info().Msg("Skipping systemd service uninstallation - not on Linux") - return nil - } - - if os.Geteuid() != 0 { - log.Info().Msg("Skipping systemd service uninstallation - not running as root/sudo") - return nil - } - - // Stop the service if it's running - stopCmd := exec.Command("systemctl", "stop", "infisical-gateway") - if err := stopCmd.Run(); err != nil { - log.Warn().Msgf("Failed to stop service: %v", err) - } - - // Disable the service - disableCmd := exec.Command("systemctl", "disable", "infisical-gateway") - if err := disableCmd.Run(); err != nil { - log.Warn().Msgf("Failed to disable service: %v", err) - } - - // Remove the service file - servicePath := "/etc/systemd/system/infisical-gateway.service" - if err := os.Remove(servicePath); err != nil && !os.IsNotExist(err) { - return fmt.Errorf("failed to remove systemd service file: %v", err) - } - - // Remove the configuration file - configPath := "/etc/infisical/gateway.conf" - if err := os.Remove(configPath); err != nil && !os.IsNotExist(err) { - return fmt.Errorf("failed to remove config file: %v", err) - } - - // Reload systemd to apply changes - reloadCmd := exec.Command("systemctl", "daemon-reload") - if err := reloadCmd.Run(); err != nil { - return fmt.Errorf("failed to reload systemd: %v", err) - } - - log.Info().Msg("Successfully uninstalled Infisical Gateway systemd service") - return nil -} diff --git a/cli/packages/gateway/udp_listener/listener_unix.go b/cli/packages/gateway/udp_listener/listener_unix.go deleted file mode 100644 index 8de2828b4..000000000 --- a/cli/packages/gateway/udp_listener/listener_unix.go +++ /dev/null @@ -1,26 +0,0 @@ -//go:build !windows -// +build !windows - -package udplistener - -import ( - "net" - "syscall" - - "golang.org/x/sys/unix" - // other imports -) - -func SetupListenerConfig() *net.ListenConfig { - return &net.ListenConfig{ - Control: func(network, address string, conn syscall.RawConn) error { - var operr error - if err := conn.Control(func(fd uintptr) { - operr = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEPORT, 1) - }); err != nil { - return err - } - return operr - }, - } -} diff --git a/cli/packages/gateway/udp_listener/listener_windows.go b/cli/packages/gateway/udp_listener/listener_windows.go deleted file mode 100644 index 4904d12e0..000000000 --- a/cli/packages/gateway/udp_listener/listener_windows.go +++ /dev/null @@ -1,18 +0,0 @@ -//go:build windows -// +build windows - -package udplistener - -import ( - "fmt" - "net" - "syscall" -) - -func SetupListenerConfig() *net.ListenConfig { - return &net.ListenConfig{ - Control: func(network, address string, conn syscall.RawConn) error { - return fmt.Errorf("Infisical relay not supported for windows.") - }, - } -} diff --git a/cli/packages/models/cli.go b/cli/packages/models/cli.go deleted file mode 100644 index 8b9fef6f6..000000000 --- a/cli/packages/models/cli.go +++ /dev/null @@ -1,161 +0,0 @@ -package models - -import "time" - -type UserCredentials struct { - Email string `json:"email"` - PrivateKey string `json:"privateKey"` - JTWToken string `json:"JTWToken"` - RefreshToken string `json:"RefreshToken"` -} - -// The file struct for Infisical config file -type ConfigFile struct { - LoggedInUserEmail string `json:"loggedInUserEmail"` - LoggedInUserDomain string `json:"LoggedInUserDomain,omitempty"` - LoggedInUsers []LoggedInUser `json:"loggedInUsers,omitempty"` - VaultBackendType string `json:"vaultBackendType,omitempty"` - VaultBackendPassphrase string `json:"vaultBackendPassphrase,omitempty"` - Domains []string `json:"domains,omitempty"` -} - -type LoggedInUser struct { - Email string `json:"email"` - Domain string `json:"domain"` -} - -type SingleEnvironmentVariable struct { - Key string `json:"key"` - WorkspaceId string `json:"workspace"` - Value string `json:"value"` - Type string `json:"type"` - ID string `json:"_id"` - SecretPath string `json:"secretPath"` - Tags []struct { - ID string `json:"_id"` - Name string `json:"name"` - Slug string `json:"slug"` - Workspace string `json:"workspace"` - } `json:"tags"` - Comment string `json:"comment"` - Etag string `json:"Etag"` -} - -type PlaintextSecretResult struct { - Secrets []SingleEnvironmentVariable - Etag string -} - -type DynamicSecret struct { - Id string `json:"id"` - DefaultTTL string `json:"defaultTTL"` - MaxTTL string `json:"maxTTL"` - Type string `json:"type"` -} - -type DynamicSecretLease struct { - Lease struct { - Id string `json:"id"` - ExpireAt time.Time `json:"expireAt"` - } `json:"lease"` - DynamicSecret DynamicSecret `json:"dynamicSecret"` - // this is a varying dict based on provider - Data map[string]interface{} `json:"data"` -} - -type TokenDetails struct { - Type string - Token string - Source string -} - -type SingleFolder struct { - ID string `json:"_id"` - Name string `json:"name"` -} - -type Workspace struct { - ID string `json:"_id"` - Name string `json:"name"` - Plan string `json:"plan,omitempty"` - V int `json:"__v"` - OrganizationId string `json:"orgId"` -} - -type WorkspaceConfigFile struct { - WorkspaceId string `json:"workspaceId"` - DefaultEnvironment string `json:"defaultEnvironment"` - GitBranchToEnvironmentMapping map[string]string `json:"gitBranchToEnvironmentMapping"` -} - -type SymmetricEncryptionResult struct { - CipherText []byte `json:"CipherText"` - Nonce []byte `json:"Nonce"` - AuthTag []byte `json:"AuthTag"` -} - -type GetAllSecretsParameters struct { - Environment string - EnvironmentPassedViaFlag bool - InfisicalToken string - UniversalAuthAccessToken string - TagSlugs string - WorkspaceId string - SecretsPath string - IncludeImport bool - Recursive bool - ExpandSecretReferences bool -} - -type InjectableEnvironmentResult struct { - Variables []string - ETag string - SecretsCount int -} - -type GetAllFoldersParameters struct { - WorkspaceId string - Environment string - FoldersPath string - InfisicalToken string - UniversalAuthAccessToken string -} - -type CreateFolderParameters struct { - FolderName string - WorkspaceId string - Environment string - FolderPath string - InfisicalToken string -} - -type DeleteFolderParameters struct { - FolderName string - WorkspaceId string - Environment string - FolderPath string - InfisicalToken string -} - -type ExpandSecretsAuthentication struct { - InfisicalToken string - UniversalAuthAccessToken string -} - -type MachineIdentityCredentials struct { - ClientId string - ClientSecret string -} - -type SecretSetOperation struct { - SecretKey string - SecretValue string - SecretOperation string -} - -type BackupSecretKeyRing struct { - ProjectID string `json:"projectId"` - Environment string `json:"environment"` - SecretPath string `json:"secretPath"` - Secrets []SingleEnvironmentVariable -} diff --git a/cli/packages/srp/client.go b/cli/packages/srp/client.go deleted file mode 100644 index 823f8f6fa..000000000 --- a/cli/packages/srp/client.go +++ /dev/null @@ -1,140 +0,0 @@ -package srp - -import ( - "bytes" - "errors" - "math/big" -) - -type SRPClient struct { - Params *SRPParams - Secret1 *big.Int - Multiplier *big.Int - A *big.Int - X *big.Int - M1 []byte - M2 []byte - K []byte - u *big.Int - s *big.Int -} - -func NewClient(params *SRPParams, identity, password, secret1 []byte) *SRPClient { - multiplier := getMultiplier(params) - secret1Int := intFromBytes(secret1) - Ab := getA(params, secret1Int) - A := intFromBytes(Ab) - x := getx(params, []byte(""), identity, password) // salt has to be set using SetSalt - - return &SRPClient{ - Params: params, - Multiplier: multiplier, - Secret1: secret1Int, - A: A, - X: x, - } -} - -func (c *SRPClient) ComputeA() []byte { - return intToBytes(c.A) -} - -// ComputeVerifier returns a verifier that is calculated as described in -// Section 3 of [SRP-RFC] -func ComputeVerifier(params *SRPParams, salt, identity, password []byte) []byte { - x := getx(params, salt, identity, password) - vNum := new(big.Int) - vNum.Exp(params.G, x, params.N) - - return padToN(vNum, params) -} - -func (c *SRPClient) SetB(Bb []byte) { - B := intFromBytes(Bb) - u := getu(c.Params, c.A, B) - S := clientGetS(c.Params, c.Multiplier, c.X, c.Secret1, B, u) - - c.K = getK(c.Params, S) - c.M1 = getM1(c.Params, intToBytes(c.A), Bb, c.K) // modified S -> c.K - c.M2 = getM2(c.Params, intToBytes(c.A), c.M1, c.K) - - c.u = u // Only for tests - c.s = intFromBytes(S) // Only for tests -} - -func (c *SRPClient) SetSalt(salt, identity, password []byte) { - c.X = getx(c.Params, salt, identity, password) //Overwrite -} - -func (c *SRPClient) ComputeM1() []byte { - if c.M1 == nil { - panic("Incomplete protocol") - } - - return c.M1 -} - -func (c *SRPClient) ComputeK() []byte { - return c.K -} - -func (c *SRPClient) CheckM2(M2 []byte) error { - if !bytes.Equal(c.M2, M2) { - return errors.New("M2 didn't check") - } else { - return nil - } -} - -func getA(params *SRPParams, a *big.Int) []byte { - ANum := new(big.Int) - ANum.Exp(params.G, a, params.N) - return padToN(ANum, params) -} - -func clientGetS(params *SRPParams, k, x, a, B, u *big.Int) []byte { - BLessThan0 := B.Cmp(big.NewInt(0)) <= 0 - NLessThanB := params.N.Cmp(B) <= 0 - if BLessThan0 || NLessThanB { - panic("invalid server-supplied 'B', must be 1..N-1") - } - - result1 := new(big.Int) - result1.Exp(params.G, x, params.N) - - result2 := new(big.Int) - result2.Mul(k, result1) - - result3 := new(big.Int) - result3.Sub(B, result2) - - result4 := new(big.Int) - result4.Mul(u, x) - - result5 := new(big.Int) - result5.Add(a, result4) - - result6 := new(big.Int) - result6.Exp(result3, result5, params.N) - - result7 := new(big.Int) - result7.Mod(result6, params.N) - - return padToN(result7, params) -} - -func getx(params *SRPParams, salt, I, P []byte) *big.Int { - var ipBytes []byte - ipBytes = append(ipBytes, I...) - ipBytes = append(ipBytes, []byte(":")...) - ipBytes = append(ipBytes, P...) - - hashIP := params.Hash.New() - hashIP.Write(ipBytes) - - hashX := params.Hash.New() - hashX.Write(salt) - hashX.Write(hashToBytes(hashIP)) - - return hashToInt(hashX) -} diff --git a/cli/packages/srp/params.go b/cli/packages/srp/params.go deleted file mode 100644 index 0fca25302..000000000 --- a/cli/packages/srp/params.go +++ /dev/null @@ -1,95 +0,0 @@ -package srp - -import ( - "crypto" - "fmt" - "math/big" -) - -// Map of bits to tuple -type SRPParams struct { - G *big.Int - N *big.Int - Hash crypto.Hash - NLengthBits int -} - -var knownGroups map[int]*SRPParams - -func createParams(G int64, nBitLength int, hash crypto.Hash, NHex string) *SRPParams { - p := SRPParams{ - G: big.NewInt(G), - N: new(big.Int), - NLengthBits: nBitLength, - Hash: hash, - } - - b := bytesFromHexString(NHex) - p.N.SetBytes(b) - return &p -} - -func GetParams(G int) *SRPParams { - params := knownGroups[G] - if params == nil { - panic(fmt.Sprintf("Params don't exist for %v", G)) - } else { - return params - } -} - -func init() { - knownGroups = make(map[int]*SRPParams) - - knownGroups[1024] = createParams(2, 1024, crypto.SHA1, ` - EEAF0AB9 ADB38DD6 9C33F80A FA8FC5E8 60726187 75FF3C0B 9EA2314C - 9C256576 D674DF74 96EA81D3 383B4813 D692C6E0 E0D5D8E2 50B98BE4 - 8E495C1D 6089DAD1 5DC7D7B4 6154D6B6 CE8EF4AD 69B15D49 82559B29 - 7BCF1885 C529F566 660E57EC 68EDBC3C 05726CC0 2FD4CBF4 976EAA9A - FD5138FE 8376435B 9FC61D2F C0EB06E3`) - - knownGroups[1536] = createParams(2, 1536, crypto.SHA1, ` - 9DEF3CAF B939277A B1F12A86 17A47BBB DBA51DF4 99AC4C80 BEEEA961 - 4B19CC4D 5F4F5F55 6E27CBDE 51C6A94B E4607A29 1558903B A0D0F843 - 80B655BB 9A22E8DC DF028A7C EC67F0D0 8134B1C8 B9798914 9B609E0B - E3BAB63D 47548381 DBC5B1FC 764E3F4B 53DD9DA1 158BFD3E 2B9C8CF5 - 6EDF0195 39349627 DB2FD53D 24B7C486 65772E43 7D6C7F8C E442734A - F7CCB7AE 837C264A E3A9BEB8 7F8A2FE9 B8B5292E 5A021FFF 5E91479E - 8CE7A28C 2442C6F3 15180F93 499A234D CF76E3FE D135F9BB - `) - - knownGroups[2048] = createParams(2, 2048, crypto.SHA256, ` - AC6BDB41 324A9A9B F166DE5E 1389582F AF72B665 1987EE07 FC319294 - 3DB56050 A37329CB B4A099ED 8193E075 7767A13D D52312AB 4B03310D - CD7F48A9 DA04FD50 E8083969 EDB767B0 CF609517 9A163AB3 661A05FB - D5FAAAE8 2918A996 2F0B93B8 55F97993 EC975EEA A80D740A DBF4FF74 - 7359D041 D5C33EA7 1D281E44 6B14773B CA97B43A 23FB8016 76BD207A - 436C6481 F1D2B907 8717461A 5B9D32E6 88F87748 544523B5 24B0D57D - 5EA77A27 75D2ECFA 032CFBDB F52FB378 61602790 04E57AE6 AF874E73 - 03CE5329 9CCC041C 7BC308D8 2A5698F3 A8D0C382 71AE35F8 E9DBFBB6 - 94B5C803 D89F7AE4 35DE236D 525F5475 9B65E372 FCD68EF2 0FA7111F - 9E4AFF73 - `) - - knownGroups[4096] = createParams(5, 4096, crypto.SHA256, ` - FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08 - 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B - 302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9 - A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 - 49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 - FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D - 670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C - 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718 - 3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D - 04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D - B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 - 1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C - BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC - E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7 88719A10 BDBA5B26 - 99C32718 6AF4E23C 1A946834 B6150BDA 2583E9CA 2AD44CE8 DBBBC2DB - 04DE8EF9 2E8EFC14 1FBECAA6 287C5947 4E6BC05D 99B2964F A090C3A2 - 233BA186 515BE7ED 1F612970 CEE2D7AF B81BDD76 2170481C D0069127 - D5B05AA9 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34063199 - FFFFFFFF FFFFFFFF - `) -} diff --git a/cli/packages/srp/server.go b/cli/packages/srp/server.go deleted file mode 100644 index b8cdbe84d..000000000 --- a/cli/packages/srp/server.go +++ /dev/null @@ -1,104 +0,0 @@ -package srp - -import ( - "bytes" - "errors" - "math/big" -) - -type SRPServer struct { - Params *SRPParams - Verifier *big.Int - Secret2 *big.Int - B *big.Int - M1 []byte - M2 []byte - K []byte - u *big.Int - s *big.Int -} - -func NewServer(params *SRPParams, Vb []byte, S2b []byte) *SRPServer { - multiplier := getMultiplier(params) - V := intFromBytes(Vb) - secret2 := intFromBytes(S2b) - - Bb := getB(params, multiplier, V, secret2) - B := intFromBytes(Bb) - - return &SRPServer{ - Params: params, - Secret2: secret2, - Verifier: V, - B: B, - } -} - -func (s *SRPServer) ComputeB() []byte { - return intToBytes(s.B) -} - -func (s *SRPServer) SetA(A []byte) { - AInt := intFromBytes(A) - U := getu(s.Params, AInt, s.B) - S := serverGetS(s.Params, s.Verifier, AInt, s.Secret2, U) - - s.K = getK(s.Params, S) - s.M1 = getM1(s.Params, A, intToBytes(s.B), S) - s.M2 = getM2(s.Params, A, s.M1, s.K) - - s.u = U // only for tests - s.s = intFromBytes(S) // only for tests -} - -func (s *SRPServer) CheckM1(M1 []byte) ([]byte, error) { - if !bytes.Equal(s.M1, M1) { - return nil, errors.New("Client did not use the same password") - } else { - return s.M2, nil - } -} - -func (s *SRPServer) ComputeK() []byte { - return s.K -} - -// Helpers - -func serverGetS(params *SRPParams, V, A, S2, U *big.Int) []byte { - ALessThan0 := A.Cmp(big.NewInt(0)) <= 0 - NLessThanA := params.N.Cmp(A) <= 0 - if ALessThan0 || NLessThanA { - panic("invalid client-supplied 'A', must be 1..N-1") - } - - result1 := new(big.Int) - result1.Exp(V, U, params.N) - - result2 := new(big.Int) - result2.Mul(A, result1) - - result3 := new(big.Int) - result3.Exp(result2, S2, params.N) - - result4 := new(big.Int) - result4.Mod(result3, params.N) - - return padToN(result4, params) -} - -func getB(params *SRPParams, multiplier, V, b *big.Int) []byte { - gModPowB := new(big.Int) - gModPowB.Exp(params.G, b, params.N) - - kMulV := new(big.Int) - kMulV.Mul(multiplier, V) - - leftSide := new(big.Int) - leftSide.Add(kMulV, gModPowB) - - final := new(big.Int) - final.Mod(leftSide, params.N) - - return padToN(final, params) -} diff --git a/cli/packages/srp/srp.go b/cli/packages/srp/srp.go deleted file mode 100644 index 448a94d1a..000000000 --- a/cli/packages/srp/srp.go +++ /dev/null @@ -1,103 +0,0 @@ -// Package srp is port of node-srp to Go. -// -// To use SRP, first decide on they parameters you will use. Both client and server must -// use the same set. -// -// params := srp.GetParams(4096) -// -// From the client... generate a new secret key, initialize the client, and compute A. -// Once you have A, you can send A to the server. -// -// secret1 := srp.GenKey() -// client := NewClient(params, salt, identity, secret, a) -// srpA := client.computeA() -// -// sendToServer(srpA) -// -// From the server... generate another secret key, initialize the server, and compute B. -// Once you have B, you can send B to the client. -// -// secret2 := srp.GenKey() -// server := NewServer(params, verifier, secret2) -// srpB := client.computeB() -// -// sendToClient(srpB) -// -// Once the client received B from the server, it can compute M1 based on A and B. -// Once you have M1, send M1 to the server. -// -// client.setB(srpB) -// srpM1 := client.ComputeM1() -// sendM1ToServer(srpM1) -// -// Once the server receives M1, it can verify that it is correct. If checkM1() returns -// an error, authentication failed. If it succeeds it should be sent to the client. -// -// srpM2, err := server.checkM1(srpM1) -// -// Once the client receives M2, it can verify that it is correct, and know that authentication -// was successful. -// -// err = client.CheckM2(serverM2) -// -// Now that both client and server have completed a successful authentication, they can -// both compute K independently. K can now be used as either a key to encrypt communication -// or as a session ID. -// -// clientK := client.ComputeK() -// serverK := server.ComputeK() -package srp - -import ( - "crypto/rand" - "io" - "math/big" -) - -func GenKey() []byte { - bytes := make([]byte, 32) - _, err := io.ReadFull(rand.Reader, bytes) - if err != nil { - panic("Random source is broken!") - } - - return bytes -} - -func getK(params *SRPParams, S []byte) []byte { - hashK := params.Hash.New() - hashK.Write(S) - return hashToBytes(hashK) -} - -func getu(params *SRPParams, A, B *big.Int) *big.Int { - hashU := params.Hash.New() - hashU.Write(A.Bytes()) - hashU.Write(B.Bytes()) - - return hashToInt(hashU) -} - -func getM1(params *SRPParams, A, B, S []byte) []byte { - hashM1 := params.Hash.New() - hashM1.Write(A) - hashM1.Write(B) - hashM1.Write(S) - return hashToBytes(hashM1) -} - -func getM2(params *SRPParams, A, M, K []byte) []byte { - hashM1 := params.Hash.New() - hashM1.Write(A) - hashM1.Write(M) - hashM1.Write(K) - return hashToBytes(hashM1) -} - -func getMultiplier(params *SRPParams) *big.Int { - hashK := params.Hash.New() - hashK.Write(padToN(params.N, params)) - hashK.Write(padToN(params.G, params)) - - return hashToInt(hashK) -} diff --git a/cli/packages/srp/util.go b/cli/packages/srp/util.go deleted file mode 100644 index 60929bfd5..000000000 --- a/cli/packages/srp/util.go +++ /dev/null @@ -1,48 +0,0 @@ -package srp - -import ( - "encoding/hex" - "hash" - "math/big" - "regexp" -) - -// Helpers - -func padTo(bytes []byte, length int) []byte { - paddingLength := length - len(bytes) - padding := make([]byte, paddingLength, paddingLength) - - return append(padding, bytes...) -} - -func padToN(number *big.Int, params *SRPParams) []byte { - return padTo(number.Bytes(), params.NLengthBits/8) -} - -func hashToBytes(h hash.Hash) []byte { - return h.Sum(nil) -} - -func hashToInt(h hash.Hash) *big.Int { - U := new(big.Int) - U.SetBytes(hashToBytes(h)) - return U -} - -func intFromBytes(bytes []byte) *big.Int { - i := new(big.Int) - i.SetBytes(bytes) - return i -} - -func intToBytes(i *big.Int) []byte { - return i.Bytes() -} - -func bytesFromHexString(s string) []byte { - re, _ := regexp.Compile("[^0-9a-fA-F]") - h := re.ReplaceAll([]byte(s), []byte("")) - b, _ := hex.DecodeString(string(h)) - return b -} diff --git a/cli/packages/systemd/daemon.go b/cli/packages/systemd/daemon.go deleted file mode 100644 index ce3c97394..000000000 --- a/cli/packages/systemd/daemon.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2014 Docker, Inc. -// Copyright 2015-2018 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -// Package daemon provides a Go implementation of the sd_notify protocol. -// It can be used to inform systemd of service start-up completion, watchdog -// events, and other status changes. -// -// https://www.freedesktop.org/software/systemd/man/sd_notify.html#Description -package systemd - -import ( - "net" - "os" -) - -const ( - // SdNotifyReady tells the service manager that service startup is finished - // or the service finished loading its configuration. - SdNotifyReady = "READY=1" - - // SdNotifyStopping tells the service manager that the service is beginning - // its shutdown. - SdNotifyStopping = "STOPPING=1" - - // SdNotifyReloading tells the service manager that this service is - // reloading its configuration. Note that you must call SdNotifyReady when - // it completed reloading. - SdNotifyReloading = "RELOADING=1" - - // SdNotifyWatchdog tells the service manager to update the watchdog - // timestamp for the service. - SdNotifyWatchdog = "WATCHDOG=1" -) - -// SdNotify sends a message to the init daemon. It is common to ignore the error. -// If `unsetEnvironment` is true, the environment variable `NOTIFY_SOCKET` -// will be unconditionally unset. -// -// It returns one of the following: -// (false, nil) - notification not supported (i.e. NOTIFY_SOCKET is unset) -// (false, err) - notification supported, but failure happened (e.g. error connecting to NOTIFY_SOCKET or while sending data) -// (true, nil) - notification supported, data has been sent -func SdNotify(unsetEnvironment bool, state string) (bool, error) { - socketAddr := &net.UnixAddr{ - Name: os.Getenv("NOTIFY_SOCKET"), - Net: "unixgram", - } - - // NOTIFY_SOCKET not set - if socketAddr.Name == "" { - return false, nil - } - - if unsetEnvironment { - if err := os.Unsetenv("NOTIFY_SOCKET"); err != nil { - return false, err - } - } - - conn, err := net.DialUnix(socketAddr.Net, nil, socketAddr) - // Error connecting to NOTIFY_SOCKET - if err != nil { - return false, err - } - defer conn.Close() - - if _, err = conn.Write([]byte(state)); err != nil { - return false, err - } - return true, nil -} diff --git a/cli/packages/telemetry/telemetry.go b/cli/packages/telemetry/telemetry.go deleted file mode 100644 index ffd743457..000000000 --- a/cli/packages/telemetry/telemetry.go +++ /dev/null @@ -1,82 +0,0 @@ -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 -} diff --git a/cli/packages/util/agent.go b/cli/packages/util/agent.go deleted file mode 100644 index 215e43551..000000000 --- a/cli/packages/util/agent.go +++ /dev/null @@ -1,41 +0,0 @@ -package util - -import ( - "fmt" - "strconv" - "time" -) - -// ConvertPollingIntervalToTime converts a string representation of a polling interval to a time.Duration -func ConvertPollingIntervalToTime(pollingInterval string) (time.Duration, error) { - length := len(pollingInterval) - if length < 2 { - return 0, fmt.Errorf("invalid format") - } - - unit := pollingInterval[length-1:] - numberPart := pollingInterval[:length-1] - - number, err := strconv.Atoi(numberPart) - if err != nil { - return 0, err - } - - switch unit { - case "s": - if number < 60 { - return 0, fmt.Errorf("polling interval must be at least 60 seconds") - } - return time.Duration(number) * time.Second, nil - case "m": - return time.Duration(number) * time.Minute, nil - case "h": - return time.Duration(number) * time.Hour, nil - case "d": - return time.Duration(number) * 24 * time.Hour, nil - case "w": - return time.Duration(number) * 7 * 24 * time.Hour, nil - default: - return 0, fmt.Errorf("invalid time unit") - } -} diff --git a/cli/packages/util/auth.go b/cli/packages/util/auth.go deleted file mode 100644 index eaf7cecc1..000000000 --- a/cli/packages/util/auth.go +++ /dev/null @@ -1,208 +0,0 @@ -package util - -import ( - "fmt" - "os" - "os/exec" - - infisicalSdk "github.com/infisical/go-sdk" - "github.com/rs/zerolog/log" - "github.com/spf13/cobra" -) - -type AuthStrategyType string - -var AuthStrategy = struct { - UNIVERSAL_AUTH AuthStrategyType - KUBERNETES_AUTH AuthStrategyType - AZURE_AUTH AuthStrategyType - GCP_ID_TOKEN_AUTH AuthStrategyType - GCP_IAM_AUTH AuthStrategyType - AWS_IAM_AUTH AuthStrategyType - OIDC_AUTH AuthStrategyType - JWT_AUTH AuthStrategyType -}{ - UNIVERSAL_AUTH: "universal-auth", - KUBERNETES_AUTH: "kubernetes", - AZURE_AUTH: "azure", - GCP_ID_TOKEN_AUTH: "gcp-id-token", - GCP_IAM_AUTH: "gcp-iam", - AWS_IAM_AUTH: "aws-iam", - OIDC_AUTH: "oidc-auth", - JWT_AUTH: "jwt-auth", -} - -var AVAILABLE_AUTH_STRATEGIES = []AuthStrategyType{ - AuthStrategy.UNIVERSAL_AUTH, - AuthStrategy.KUBERNETES_AUTH, - AuthStrategy.AZURE_AUTH, - AuthStrategy.GCP_ID_TOKEN_AUTH, - AuthStrategy.GCP_IAM_AUTH, - AuthStrategy.AWS_IAM_AUTH, - AuthStrategy.OIDC_AUTH, - AuthStrategy.JWT_AUTH, -} - -func IsAuthMethodValid(authMethod string, allowUserAuth bool) (isValid bool, strategy AuthStrategyType) { - - if authMethod == "user" && allowUserAuth { - return true, "" - } - - for _, strategy := range AVAILABLE_AUTH_STRATEGIES { - if string(strategy) == authMethod { - return true, strategy - } - } - return false, "" -} - -// EstablishUserLoginSession handles the login flow to either create a new session or restore an expired one. -// It returns fresh user details if login is successful. -func EstablishUserLoginSession() LoggedInUserDetails { - log.Info().Msg("No valid login session found, triggering login flow") - - exePath, err := os.Executable() - if err != nil { - PrintErrorMessageAndExit(fmt.Sprintf("Failed to determine executable path: %v", err)) - } - - // Spawn infisical login command - loginCmd := exec.Command(exePath, "login", "--silent") - loginCmd.Stdin = os.Stdin - loginCmd.Stdout = os.Stdout - loginCmd.Stderr = os.Stderr - - err = loginCmd.Run() - if err != nil { - PrintErrorMessageAndExit(fmt.Sprintf("Failed to automatically trigger login flow. Please run [infisical login] manually to login.")) - } - - loggedInUserDetails, err := GetCurrentLoggedInUserDetails(true) - if err != nil { - PrintErrorMessageAndExit("You must be logged in to run this command. To login, run [infisical login]") - } - - if loggedInUserDetails.LoginExpired { - PrintErrorMessageAndExit("Your login session has expired. Please run [infisical login]") - } - - return loggedInUserDetails -} - -type SdkAuthenticator struct { - infisicalClient infisicalSdk.InfisicalClientInterface - cmd *cobra.Command -} - -func NewSdkAuthenticator(infisicalClient infisicalSdk.InfisicalClientInterface, cmd *cobra.Command) *SdkAuthenticator { - return &SdkAuthenticator{ - infisicalClient: infisicalClient, - cmd: cmd, - } -} -func (a *SdkAuthenticator) HandleUniversalAuthLogin() (credential infisicalSdk.MachineIdentityCredential, e error) { - - clientId, err := GetCmdFlagOrEnv(a.cmd, "client-id", []string{INFISICAL_UNIVERSAL_AUTH_CLIENT_ID_NAME}) - - if err != nil { - return infisicalSdk.MachineIdentityCredential{}, err - } - - clientSecret, err := GetCmdFlagOrEnv(a.cmd, "client-secret", []string{INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET_NAME}) - if err != nil { - return infisicalSdk.MachineIdentityCredential{}, err - } - - return a.infisicalClient.Auth().UniversalAuthLogin(clientId, clientSecret) -} - -func (a *SdkAuthenticator) HandleJwtAuthLogin() (credential infisicalSdk.MachineIdentityCredential, e error) { - - identityId, err := GetCmdFlagOrEnv(a.cmd, "machine-identity-id", []string{INFISICAL_MACHINE_IDENTITY_ID_NAME}) - if err != nil { - return infisicalSdk.MachineIdentityCredential{}, err - } - - jwt, err := GetCmdFlagOrEnv(a.cmd, "jwt", []string{INFISICAL_JWT_NAME}) - if err != nil { - return infisicalSdk.MachineIdentityCredential{}, err - } - - return a.infisicalClient.Auth().JwtAuthLogin(identityId, jwt) -} - -func (a *SdkAuthenticator) HandleKubernetesAuthLogin() (credential infisicalSdk.MachineIdentityCredential, e error) { - - identityId, err := GetCmdFlagOrEnv(a.cmd, "machine-identity-id", []string{INFISICAL_MACHINE_IDENTITY_ID_NAME}) - if err != nil { - return infisicalSdk.MachineIdentityCredential{}, err - } - - serviceAccountTokenPath, err := GetCmdFlagOrEnv(a.cmd, "service-account-token-path", []string{INFISICAL_KUBERNETES_SERVICE_ACCOUNT_TOKEN_NAME}) - if err != nil { - return infisicalSdk.MachineIdentityCredential{}, err - } - - return a.infisicalClient.Auth().KubernetesAuthLogin(identityId, serviceAccountTokenPath) -} - -func (a *SdkAuthenticator) HandleAzureAuthLogin() (credential infisicalSdk.MachineIdentityCredential, e error) { - - identityId, err := GetCmdFlagOrEnv(a.cmd, "machine-identity-id", []string{INFISICAL_MACHINE_IDENTITY_ID_NAME}) - if err != nil { - return infisicalSdk.MachineIdentityCredential{}, err - } - - return a.infisicalClient.Auth().AzureAuthLogin(identityId, "") -} - -func (a *SdkAuthenticator) HandleGcpIdTokenAuthLogin() (credential infisicalSdk.MachineIdentityCredential, e error) { - - identityId, err := GetCmdFlagOrEnv(a.cmd, "machine-identity-id", []string{INFISICAL_MACHINE_IDENTITY_ID_NAME}) - if err != nil { - return infisicalSdk.MachineIdentityCredential{}, err - } - - return a.infisicalClient.Auth().GcpIdTokenAuthLogin(identityId) -} - -func (a *SdkAuthenticator) HandleGcpIamAuthLogin() (credential infisicalSdk.MachineIdentityCredential, e error) { - - identityId, err := GetCmdFlagOrEnv(a.cmd, "machine-identity-id", []string{INFISICAL_MACHINE_IDENTITY_ID_NAME}) - if err != nil { - return infisicalSdk.MachineIdentityCredential{}, err - } - - serviceAccountKeyFilePath, err := GetCmdFlagOrEnv(a.cmd, "service-account-key-file-path", []string{INFISICAL_GCP_IAM_SERVICE_ACCOUNT_KEY_FILE_PATH_NAME}) - if err != nil { - return infisicalSdk.MachineIdentityCredential{}, err - } - - return a.infisicalClient.Auth().GcpIamAuthLogin(identityId, serviceAccountKeyFilePath) -} - -func (a *SdkAuthenticator) HandleAwsIamAuthLogin() (credential infisicalSdk.MachineIdentityCredential, e error) { - - identityId, err := GetCmdFlagOrEnv(a.cmd, "machine-identity-id", []string{INFISICAL_MACHINE_IDENTITY_ID_NAME}) - if err != nil { - return infisicalSdk.MachineIdentityCredential{}, err - } - - return a.infisicalClient.Auth().AwsIamAuthLogin(identityId) -} - -func (a *SdkAuthenticator) HandleOidcAuthLogin() (credential infisicalSdk.MachineIdentityCredential, e error) { - - identityId, err := GetCmdFlagOrEnv(a.cmd, "machine-identity-id", []string{INFISICAL_MACHINE_IDENTITY_ID_NAME}) - if err != nil { - return infisicalSdk.MachineIdentityCredential{}, err - } - - jwt, err := GetCmdFlagOrEnv(a.cmd, "jwt", []string{INFISICAL_JWT_NAME, INFISICAL_OIDC_AUTH_JWT_NAME}) - if err != nil { - return infisicalSdk.MachineIdentityCredential{}, err - } - - return a.infisicalClient.Auth().OidcAuthLogin(identityId, jwt) -} diff --git a/cli/packages/util/check-for-update.go b/cli/packages/util/check-for-update.go deleted file mode 100644 index a1e35f656..000000000 --- a/cli/packages/util/check-for-update.go +++ /dev/null @@ -1,183 +0,0 @@ -package util - -import ( - "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "net/http" - "os" - "os/exec" - "runtime" - "strings" - - "github.com/fatih/color" - "github.com/rs/zerolog/log" -) - -func CheckForUpdate() { - if checkEnv := os.Getenv("INFISICAL_DISABLE_UPDATE_CHECK"); checkEnv != "" { - return - } - latestVersion, _, err := getLatestTag("Infisical", "cli") - if err != nil { - log.Debug().Err(err) - // do nothing and continue - return - } - - // daysSinceRelease, _ := daysSinceDate(publishedDate) - - if latestVersion != CLI_VERSION { - yellow := color.New(color.FgYellow).SprintFunc() - blue := color.New(color.FgCyan).SprintFunc() - black := color.New(color.FgBlack).SprintFunc() - - msg := fmt.Sprintf("%s %s %s %s", - yellow("A new release of infisical is available:"), - blue(CLI_VERSION), - black("->"), - blue(latestVersion), - ) - - fmt.Fprintln(os.Stderr, msg) - - updateInstructions := GetUpdateInstructions() - - if updateInstructions != "" { - msg = fmt.Sprintf("\n%s\n", GetUpdateInstructions()) - fmt.Fprintln(os.Stderr, msg) - } - - } -} - -func DisplayAptInstallationChangeBanner(isSilent bool) { - if isSilent { - return - } - - if runtime.GOOS == "linux" { - _, err := exec.LookPath("apt-get") - isApt := err == nil - if isApt { - yellow := color.New(color.FgYellow).SprintFunc() - msg := fmt.Sprintf("%s", - yellow("Update Required: Your current package installation script is outdated and will no longer receive updates.\nPlease update to the new installation script which can be found here https://infisical.com/docs/cli/overview#installation debian section\n"), - ) - - fmt.Fprintln(os.Stderr, msg) - } - } -} - -func getLatestTag(repoOwner string, repoName string) (string, string, error) { - url := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/latest", repoOwner, repoName) - resp, err := http.Get(url) - if err != nil { - return "", "", err - } - if resp.StatusCode != 200 { - return "", "", errors.New(fmt.Sprintf("gitHub API returned status code %d", resp.StatusCode)) - } - - defer resp.Body.Close() - - body, err := io.ReadAll(resp.Body) - if err != nil { - return "", "", err - } - - var releaseDetails struct { - TagName string `json:"tag_name"` - PublishedAt string `json:"published_at"` - } - - if err := json.Unmarshal(body, &releaseDetails); err != nil { - return "", "", fmt.Errorf("failed to unmarshal github response: %w", err) - } - - tag_prefix := "v" - - // Extract the version from the first valid tag - version := strings.TrimPrefix(releaseDetails.TagName, tag_prefix) - - return version, releaseDetails.PublishedAt, nil -} - -func GetUpdateInstructions() string { - os := runtime.GOOS - switch os { - case "darwin": - return "To update, run: brew update && brew upgrade infisical" - case "windows": - return "To update, run: scoop update infisical" - case "linux": - pkgManager := getLinuxPackageManager() - switch pkgManager { - case "apt-get": - return "To update, run: sudo apt-get update && sudo apt-get install infisical" - case "yum": - return "To update, run: sudo yum update infisical" - case "apk": - return "To update, run: sudo apk update && sudo apk upgrade infisical" - case "yay": - return "To update, run: yay -Syu infisical" - default: - return "" - } - default: - return "" - } -} - -func getLinuxPackageManager() string { - cmd := exec.Command("apt-get", "--version") - if err := cmd.Run(); err == nil { - return "apt-get" - } - - cmd = exec.Command("yum", "--version") - if err := cmd.Run(); err == nil { - return "yum" - } - - cmd = exec.Command("yay", "--version") - if err := cmd.Run(); err == nil { - return "yay" - } - - cmd = exec.Command("apk", "--version") - if err := cmd.Run(); err == nil { - return "apk" - } - - return "" -} - -func IsRunningInDocker() bool { - if _, err := os.Stat("/.dockerenv"); err == nil { - return true - } - - cgroup, err := ioutil.ReadFile("/proc/self/cgroup") - if err != nil { - return false - } - - return strings.Contains(string(cgroup), "docker") -} - -// func daysSinceDate(dateString string) (int, error) { -// layout := "2006-01-02T15:04:05Z" -// parsedDate, err := time.Parse(layout, dateString) -// if err != nil { -// return 0, err -// } - -// currentTime := time.Now() -// difference := currentTime.Sub(parsedDate) -// days := int(difference.Hours() / 24) -// return days, nil -// } diff --git a/cli/packages/util/common.go b/cli/packages/util/common.go deleted file mode 100644 index 07618ae87..000000000 --- a/cli/packages/util/common.go +++ /dev/null @@ -1,117 +0,0 @@ -package util - -import ( - "fmt" - "net/http" - "os" - "strings" - "unicode" - - "github.com/Infisical/infisical-merge/packages/config" - "github.com/go-resty/resty/v2" -) - -func GetHomeDir() (string, error) { - directory, err := os.UserHomeDir() - return directory, err -} - -// write file to given path. If path does not exist throw error -func WriteToFile(fileName string, dataToWrite []byte, filePerm os.FileMode) error { - err := os.WriteFile(fileName, dataToWrite, filePerm) - if err != nil { - return fmt.Errorf("unable to wrote to file [err=%v]", err) - } - - return nil -} - -func ValidateInfisicalAPIConnection() (ok bool) { - _, err := http.Get(fmt.Sprintf("%v/status", config.INFISICAL_URL)) - return err == nil -} - -func GetRestyClientWithCustomHeaders() (*resty.Client, error) { - httpClient := resty.New() - customHeaders := os.Getenv("INFISICAL_CUSTOM_HEADERS") - if customHeaders != "" { - headers, err := GetInfisicalCustomHeadersMap() - if err != nil { - return nil, err - } - - httpClient.SetHeaders(headers) - } - return httpClient, nil -} - -func GetInfisicalCustomHeadersMap() (map[string]string, error) { - customHeaders := os.Getenv("INFISICAL_CUSTOM_HEADERS") - if customHeaders == "" { - return nil, nil - } - - headers := map[string]string{} - - pos := 0 - for pos < len(customHeaders) { - for pos < len(customHeaders) && unicode.IsSpace(rune(customHeaders[pos])) { - pos++ - } - - if pos >= len(customHeaders) { - break - } - - keyStart := pos - for pos < len(customHeaders) && customHeaders[pos] != '=' && !unicode.IsSpace(rune(customHeaders[pos])) { - pos++ - } - - if pos >= len(customHeaders) || customHeaders[pos] != '=' { - return nil, fmt.Errorf("invalid custom header format. Expected \"headerKey1=value1 headerKey2=value2 ....\" but got %v", customHeaders) - } - - key := customHeaders[keyStart:pos] - pos++ - - for pos < len(customHeaders) && unicode.IsSpace(rune(customHeaders[pos])) { - pos++ - } - - var value string - - if pos < len(customHeaders) { - if customHeaders[pos] == '"' || customHeaders[pos] == '\'' { - quoteChar := customHeaders[pos] - pos++ - valueStart := pos - - for pos < len(customHeaders) && - (customHeaders[pos] != quoteChar || - (pos > 0 && customHeaders[pos-1] == '\\')) { - pos++ - } - - if pos < len(customHeaders) { - value = customHeaders[valueStart:pos] - pos++ - } else { - value = customHeaders[valueStart:] - } - } else { - valueStart := pos - for pos < len(customHeaders) && !unicode.IsSpace(rune(customHeaders[pos])) { - pos++ - } - value = customHeaders[valueStart:pos] - } - } - - if key != "" && !strings.EqualFold(key, "User-Agent") && !strings.EqualFold(key, "Accept") { - headers[key] = value - } - } - - return headers, nil -} diff --git a/cli/packages/util/config.go b/cli/packages/util/config.go deleted file mode 100644 index 8d44c84d1..000000000 --- a/cli/packages/util/config.go +++ /dev/null @@ -1,259 +0,0 @@ -package util - -import ( - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "os" - "path/filepath" - - "github.com/Infisical/infisical-merge/packages/config" - "github.com/Infisical/infisical-merge/packages/models" - "github.com/rs/zerolog/log" -) - -func WriteInitalConfig(userCredentials *models.UserCredentials) error { - fullConfigFilePath, fullConfigFileDirPath, err := GetFullConfigFilePath() - if err != nil { - return err - } - - // create directory - if _, err := os.Stat(fullConfigFileDirPath); errors.Is(err, os.ErrNotExist) { - err := os.Mkdir(fullConfigFileDirPath, os.ModePerm) - if err != nil { - return err - } - } - - // get existing config - existingConfigFile, err := GetConfigFile() - if err != nil { - return fmt.Errorf("writeInitalConfig: unable to write config file because [err=%s]", err) - } - - //if profiles exists - loggedInUser := models.LoggedInUser{ - Email: userCredentials.Email, - Domain: config.INFISICAL_URL, - } - //if empty or if email not in loggedinUsers - if len(existingConfigFile.LoggedInUsers) == 0 || !ConfigContainsEmail(existingConfigFile.LoggedInUsers, userCredentials.Email) { - existingConfigFile.LoggedInUsers = append(existingConfigFile.LoggedInUsers, loggedInUser) - } else { - //if exists update domain of loggedin users - for idx, user := range existingConfigFile.LoggedInUsers { - if user.Email == userCredentials.Email { - existingConfigFile.LoggedInUsers[idx] = loggedInUser - } - } - } - - configFile := models.ConfigFile{ - LoggedInUserEmail: userCredentials.Email, - LoggedInUserDomain: config.INFISICAL_URL, - LoggedInUsers: existingConfigFile.LoggedInUsers, - VaultBackendType: existingConfigFile.VaultBackendType, - VaultBackendPassphrase: existingConfigFile.VaultBackendPassphrase, - Domains: existingConfigFile.Domains, - } - - configFileMarshalled, err := json.Marshal(configFile) - if err != nil { - return err - } - - // Create file in directory - err = WriteToFile(fullConfigFilePath, configFileMarshalled, 0600) - if err != nil { - return err - } - - return err -} - -func ConfigFileExists() bool { - fullConfigFileURI, _, err := GetFullConfigFilePath() - if err != nil { - log.Debug().Err(err).Msgf("There was an error when creating the full path to config file") - return false - } - - if _, err := os.Stat(fullConfigFileURI); err == nil { - return true - } else { - return false - } -} - -func WorkspaceConfigFileExistsInCurrentPath() bool { - if _, err := os.Stat(INFISICAL_WORKSPACE_CONFIG_FILE_NAME); err == nil { - return true - } else { - log.Debug().Err(err) - return false - } -} - -func GetWorkSpaceFromFile() (models.WorkspaceConfigFile, error) { - cfgFile, err := FindWorkspaceConfigFile() - if err != nil { - return models.WorkspaceConfigFile{}, err - } - - configFileAsBytes, err := os.ReadFile(cfgFile) - if err != nil { - return models.WorkspaceConfigFile{}, err - } - - var workspaceConfigFile models.WorkspaceConfigFile - err = json.Unmarshal(configFileAsBytes, &workspaceConfigFile) - if err != nil { - return models.WorkspaceConfigFile{}, err - } - - return workspaceConfigFile, nil -} - -func GetWorkSpaceFromFilePath(configFileDir string) (models.WorkspaceConfigFile, error) { - configFilePath := filepath.Join(configFileDir, ".infisical.json") - - _, configFileStatusError := os.Stat(configFilePath) - if os.IsNotExist(configFileStatusError) { - return models.WorkspaceConfigFile{}, fmt.Errorf("file %s does not exist", configFilePath) - } - - configFileAsBytes, err := os.ReadFile(configFilePath) - if err != nil { - return models.WorkspaceConfigFile{}, err - } - - var workspaceConfigFile models.WorkspaceConfigFile - err = json.Unmarshal(configFileAsBytes, &workspaceConfigFile) - if err != nil { - return models.WorkspaceConfigFile{}, err - } - - return workspaceConfigFile, nil -} - -// FindWorkspaceConfigFile searches for a .infisical.json file in the current directory and all parent directories. -func FindWorkspaceConfigFile() (string, error) { - dir, err := os.Getwd() - if err != nil { - return "", err - } - - for { - path := filepath.Join(dir, INFISICAL_WORKSPACE_CONFIG_FILE_NAME) - _, err := os.Stat(path) - if err == nil { - // file found - log.Debug().Msgf("FindWorkspaceConfigFile: workspace file found at [path=%s]", path) - - return path, nil - } - - // check if we have reached the root directory - if dir == filepath.Dir(dir) { - break - } - - // move up one directory - dir = filepath.Dir(dir) - } - - // file not found - return "", fmt.Errorf("file not found: %s", INFISICAL_WORKSPACE_CONFIG_FILE_NAME) - -} - -func GetFullConfigFilePath() (fullPathToFile string, fullPathToDirectory string, err error) { - homeDir, err := GetHomeDir() - if err != nil { - return "", "", err - } - - fullPath := fmt.Sprintf("%s/%s/%s", homeDir, CONFIG_FOLDER_NAME, CONFIG_FILE_NAME) - fullDirPath := fmt.Sprintf("%s/%s", homeDir, CONFIG_FOLDER_NAME) - return fullPath, fullDirPath, err -} - -// Given a path to a workspace config, unmarshal workspace config -func GetWorkspaceConfigByPath(path string) (workspaceConfig models.WorkspaceConfigFile, err error) { - workspaceConfigFileAsBytes, err := os.ReadFile(path) - if err != nil { - return models.WorkspaceConfigFile{}, fmt.Errorf("GetWorkspaceConfigByPath: Unable to read workspace config file because [%s]", err) - } - - var workspaceConfigFile models.WorkspaceConfigFile - err = json.Unmarshal(workspaceConfigFileAsBytes, &workspaceConfigFile) - if err != nil { - return models.WorkspaceConfigFile{}, fmt.Errorf("GetWorkspaceConfigByPath: Unable to unmarshal workspace config file because [%s]", err) - } - - return workspaceConfigFile, nil -} - -// Get the infisical config file and if it doesn't exist, return empty config model, otherwise raise error -func GetConfigFile() (models.ConfigFile, error) { - fullConfigFilePath, _, err := GetFullConfigFilePath() - if err != nil { - return models.ConfigFile{}, err - } - - configFileAsBytes, err := os.ReadFile(fullConfigFilePath) - if err != nil { - if err, ok := err.(*os.PathError); ok { - return models.ConfigFile{}, nil - } else { - return models.ConfigFile{}, err - } - } - - var configFile models.ConfigFile - err = json.Unmarshal(configFileAsBytes, &configFile) - if err != nil { - return models.ConfigFile{}, err - } - - if configFile.VaultBackendPassphrase != "" { - decodedPassphrase, err := base64.StdEncoding.DecodeString(configFile.VaultBackendPassphrase) - if err != nil { - return models.ConfigFile{}, fmt.Errorf("GetConfigFile: Unable to decode base64 passphrase [err=%s]", err) - } - os.Setenv("INFISICAL_VAULT_FILE_PASSPHRASE", string(decodedPassphrase)) - } - - return configFile, nil -} - -// Write a ConfigFile to disk. Raise error if unable to save the model to disk -func WriteConfigFile(configFile *models.ConfigFile) error { - fullConfigFilePath, fullConfigFileDirPath, err := GetFullConfigFilePath() - if err != nil { - return fmt.Errorf("writeConfigFile: unable to write config file because an error occurred when getting config file path [err=%s]", err) - } - - configFileMarshalled, err := json.Marshal(configFile) - if err != nil { - return fmt.Errorf("writeConfigFile: unable to write config file because an error occurred when marshalling the config file [err=%s]", err) - } - - // check if config folder exists and if not create it - if _, err := os.Stat(fullConfigFileDirPath); errors.Is(err, os.ErrNotExist) { - err := os.Mkdir(fullConfigFileDirPath, os.ModePerm) - if err != nil { - return err - } - } - - // Create file in directory - err = os.WriteFile(fullConfigFilePath, configFileMarshalled, 0600) - if err != nil { - return fmt.Errorf("writeConfigFile: Unable to write to file [err=%s]", err) - } - - return nil -} diff --git a/cli/packages/util/constants.go b/cli/packages/util/constants.go deleted file mode 100644 index 383c7fc4c..000000000 --- a/cli/packages/util/constants.go +++ /dev/null @@ -1,63 +0,0 @@ -package util - -const ( - CONFIG_FILE_NAME = "infisical-config.json" - CONFIG_FOLDER_NAME = ".infisical" - INFISICAL_DEFAULT_US_URL = "https://app.infisical.com" - INFISICAL_DEFAULT_EU_URL = "https://eu.infisical.com" - INFISICAL_WORKSPACE_CONFIG_FILE_NAME = ".infisical.json" - INFISICAL_TOKEN_NAME = "INFISICAL_TOKEN" - INFISICAL_UNIVERSAL_AUTH_ACCESS_TOKEN_NAME = "INFISICAL_UNIVERSAL_AUTH_ACCESS_TOKEN" - INFISICAL_VAULT_FILE_PASSPHRASE_ENV_NAME = "INFISICAL_VAULT_FILE_PASSPHRASE" // This works because we've forked the keyring package and added support for this env variable. This explains why you won't find any occurrences of it in the CLI codebase. - - INFISICAL_BOOTSTRAP_EMAIL_NAME = "INFISICAL_ADMIN_EMAIL" - INFISICAL_BOOTSTRAP_PASSWORD_NAME = "INFISICAL_ADMIN_PASSWORD" - INFISICAL_BOOTSTRAP_ORGANIZATION_NAME = "INFISICAL_ADMIN_ORGANIZATION" - - VAULT_BACKEND_AUTO_MODE = "auto" - VAULT_BACKEND_FILE_MODE = "file" - - INFISICAL_AUTH_METHOD_NAME = "INFISICAL_AUTH_METHOD" - - // Universal Auth - INFISICAL_UNIVERSAL_AUTH_CLIENT_ID_NAME = "INFISICAL_UNIVERSAL_AUTH_CLIENT_ID" - INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET_NAME = "INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET" - - // Kubernetes auth - INFISICAL_KUBERNETES_SERVICE_ACCOUNT_TOKEN_NAME = "INFISICAL_KUBERNETES_SERVICE_ACCOUNT_TOKEN_PATH" - - // GCP Auth - INFISICAL_GCP_IAM_SERVICE_ACCOUNT_KEY_FILE_PATH_NAME = "INFISICAL_GCP_IAM_SERVICE_ACCOUNT_KEY_FILE_PATH" - - // OIDC Auth - INFISICAL_OIDC_AUTH_JWT_NAME = "INFISICAL_OIDC_AUTH_JWT" // deprecated in favor of INFISICAL_JWT - - // JWT AUTH - INFISICAL_JWT_NAME = "INFISICAL_JWT" - - INFISICAL_GATEWAY_TOKEN_NAME_LEGACY = "TOKEN" // backwards compatibility with gateway helm chart, where token was the only supported auth method - - // Generic env variable used for auth methods that require a machine identity ID - INFISICAL_MACHINE_IDENTITY_ID_NAME = "INFISICAL_MACHINE_IDENTITY_ID" - - SECRET_TYPE_PERSONAL = "personal" - SECRET_TYPE_SHARED = "shared" - KEYRING_SERVICE_NAME = "infisical" - PERSONAL_SECRET_TYPE_NAME = "personal" - SHARED_SECRET_TYPE_NAME = "shared" - - SERVICE_TOKEN_IDENTIFIER = "service-token" - UNIVERSAL_AUTH_TOKEN_IDENTIFIER = "universal-auth-token" - - INFISICAL_BACKUP_SECRET = "infisical-backup-secrets" // akhilmhdh: @depreciated remove in version v0.30 - INFISICAL_BACKUP_SECRET_ENCRYPTION_KEY = "infisical-backup-secret-encryption-key" - - KUBERNETES_SERVICE_HOST_ENV_NAME = "KUBERNETES_SERVICE_HOST" - KUBERNETES_SERVICE_PORT_HTTPS_ENV_NAME = "KUBERNETES_SERVICE_PORT_HTTPS" - KUBERNETES_SERVICE_ACCOUNT_CA_CERT_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" - KUBERNETES_SERVICE_ACCOUNT_TOKEN_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/token" -) - -var ( - CLI_VERSION = "devel" -) diff --git a/cli/packages/util/credentials.go b/cli/packages/util/credentials.go deleted file mode 100644 index cd73e47ca..000000000 --- a/cli/packages/util/credentials.go +++ /dev/null @@ -1,127 +0,0 @@ -package util - -import ( - "encoding/json" - "errors" - "fmt" - "strings" - - "github.com/Infisical/infisical-merge/packages/api" - "github.com/Infisical/infisical-merge/packages/config" - "github.com/Infisical/infisical-merge/packages/models" - "github.com/zalando/go-keyring" -) - -type LoggedInUserDetails struct { - IsUserLoggedIn bool - LoginExpired bool - UserCredentials models.UserCredentials -} - -func StoreUserCredsInKeyRing(userCred *models.UserCredentials) error { - userCredMarshalled, err := json.Marshal(userCred) - if err != nil { - return fmt.Errorf("StoreUserCredsInKeyRing: something went wrong when marshalling user creds [err=%s]", err) - } - - err = SetValueInKeyring(userCred.Email, string(userCredMarshalled)) - if err != nil { - return fmt.Errorf("StoreUserCredsInKeyRing: unable to store user credentials because [err=%s]", err) - } - - return err -} - -func GetUserCredsFromKeyRing(userEmail string) (credentials models.UserCredentials, err error) { - credentialsValue, err := GetValueInKeyring(userEmail) - if err != nil { - if err == keyring.ErrUnsupportedPlatform { - return models.UserCredentials{}, errors.New("your OS does not support keyring. Consider using a service token https://infisical.com/docs/documentation/platform/token") - } else if err == keyring.ErrNotFound { - return models.UserCredentials{}, errors.New("credentials not found in system keyring") - } else { - return models.UserCredentials{}, fmt.Errorf("something went wrong, failed to retrieve value from system keyring [error=%v]", err) - } - } - - var userCredentials models.UserCredentials - - err = json.Unmarshal([]byte(credentialsValue), &userCredentials) - if err != nil { - return models.UserCredentials{}, fmt.Errorf("getUserCredsFromKeyRing: Something went wrong when unmarshalling user creds [err=%s]", err) - } - - return userCredentials, err -} - -func GetCurrentLoggedInUserDetails(setConfigVariables bool) (LoggedInUserDetails, error) { - if ConfigFileExists() { - configFile, err := GetConfigFile() - if err != nil { - return LoggedInUserDetails{}, fmt.Errorf("getCurrentLoggedInUserDetails: unable to get logged in user from config file [err=%s]", err) - } - - if configFile.LoggedInUserEmail == "" { - return LoggedInUserDetails{}, nil - } - - userCreds, err := GetUserCredsFromKeyRing(configFile.LoggedInUserEmail) - if err != nil { - if strings.Contains(err.Error(), "credentials not found in system keyring") { - return LoggedInUserDetails{}, errors.New("we couldn't find your logged in details, try running [infisical login] then try again") - } else { - return LoggedInUserDetails{}, fmt.Errorf("failed to fetch credentials from keyring because [err=%s]", err) - } - } - - if setConfigVariables { - config.INFISICAL_URL_MANUAL_OVERRIDE = config.INFISICAL_URL - //configFile.LoggedInUserDomain - //if not empty set as infisical url - if configFile.LoggedInUserDomain != "" { - config.INFISICAL_URL = AppendAPIEndpoint(configFile.LoggedInUserDomain) - } - } - - // check to to see if the JWT is still valid - httpClient, err := GetRestyClientWithCustomHeaders() - if err != nil { - return LoggedInUserDetails{}, fmt.Errorf("getCurrentLoggedInUserDetails: unable to get client with custom headers [err=%s]", err) - } - - httpClient. - SetAuthToken(userCreds.JTWToken). - SetHeader("Accept", "application/json") - - isAuthenticated := api.CallIsAuthenticated(httpClient) - // TODO: add refresh token - // if !isAuthenticated { - // accessTokenResponse, err := api.CallGetNewAccessTokenWithRefreshToken(httpClient, userCreds.RefreshToken) - // if err == nil && 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 - LoginExpired: true, - UserCredentials: userCreds, - }, nil - } - - return LoggedInUserDetails{ - IsUserLoggedIn: true, - LoginExpired: false, - UserCredentials: userCreds, - }, nil - } else { - return LoggedInUserDetails{}, nil - } -} diff --git a/cli/packages/util/exec.go b/cli/packages/util/exec.go deleted file mode 100644 index 2cdb50f42..000000000 --- a/cli/packages/util/exec.go +++ /dev/null @@ -1,92 +0,0 @@ -package util - -import ( - "fmt" - "os" - "os/exec" - "os/signal" - "runtime" - "syscall" -) - -func RunCommand(singleCommand string, args []string, env []string, waitForExit bool) (*exec.Cmd, error) { - var c *exec.Cmd - var err error - - if singleCommand != "" { - c, err = RunCommandFromString(singleCommand, env, waitForExit) - } else { - c, err = RunCommandFromArgs(args, env, waitForExit) - } - - return c, err -} - -func IsProcessRunning(p *os.Process) bool { - err := p.Signal(syscall.Signal(0)) - return err == nil -} - -// For "infisical run -- COMMAND" -func RunCommandFromArgs(args []string, env []string, waitForExit bool) (*exec.Cmd, error) { - cmd := exec.Command(args[0], args[1:]...) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - cmd.Env = env - - err := execCommand(cmd, waitForExit) - - return cmd, err -} - -func execCommand(cmd *exec.Cmd, waitForExit bool) error { - sigChannel := make(chan os.Signal, 1) - signal.Notify(sigChannel) - - if err := cmd.Start(); err != nil { - return err - } - - go func() { - for { - sig := <-sigChannel - _ = cmd.Process.Signal(sig) // process all sigs - } - }() - - if !waitForExit { - return nil - } - - if err := cmd.Wait(); err != nil { - _ = cmd.Process.Signal(os.Kill) - return fmt.Errorf("failed to wait for command termination: %v", err) - } - - waitStatus := cmd.ProcessState.Sys().(syscall.WaitStatus) - os.Exit(waitStatus.ExitStatus()) - return nil -} - -// For "infisical run --command=COMMAND" -func RunCommandFromString(command string, env []string, waitForExit bool) (*exec.Cmd, error) { - shell := [2]string{"sh", "-c"} - if runtime.GOOS == "windows" { - shell = [2]string{"cmd", "/C"} - } else { - currentShell := os.Getenv("SHELL") - if currentShell != "" { - shell[0] = currentShell - } - } - - cmd := exec.Command(shell[0], shell[1], command) // #nosec G204 nosemgrep: semgrep_configs.prohibit-exec-command - cmd.Env = env - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - err := execCommand(cmd, waitForExit) - return cmd, err -} diff --git a/cli/packages/util/folders.go b/cli/packages/util/folders.go deleted file mode 100644 index fb4f2a322..000000000 --- a/cli/packages/util/folders.go +++ /dev/null @@ -1,281 +0,0 @@ -package util - -import ( - "fmt" - "strings" - - "github.com/Infisical/infisical-merge/packages/api" - "github.com/Infisical/infisical-merge/packages/models" - "github.com/rs/zerolog/log" -) - -func GetAllFolders(params models.GetAllFoldersParameters) ([]models.SingleFolder, error) { - - var foldersToReturn []models.SingleFolder - var folderErr error - if params.InfisicalToken == "" && params.UniversalAuthAccessToken == "" { - RequireLogin() - - log.Debug().Msg("GetAllFolders: Trying to fetch folders using logged in details") - - loggedInUserDetails, err := GetCurrentLoggedInUserDetails(true) - if err != nil { - return nil, err - } - - if loggedInUserDetails.LoginExpired { - loggedInUserDetails = EstablishUserLoginSession() - } - - if params.WorkspaceId == "" { - workspaceFile, err := GetWorkSpaceFromFile() - if err != nil { - PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag") - } - params.WorkspaceId = workspaceFile.WorkspaceId - } - - folders, err := GetFoldersViaJTW(loggedInUserDetails.UserCredentials.JTWToken, params.WorkspaceId, params.Environment, params.FoldersPath) - folderErr = err - foldersToReturn = folders - } else if params.InfisicalToken != "" { - log.Debug().Msg("GetAllFolders: Trying to fetch folders using service token") - - // get folders via service token - folders, err := GetFoldersViaServiceToken(params.InfisicalToken, params.WorkspaceId, params.Environment, params.FoldersPath) - folderErr = err - foldersToReturn = folders - } else if params.UniversalAuthAccessToken != "" { - log.Debug().Msg("GetAllFolders: Trying to fetch folders using universal auth") - - if params.WorkspaceId == "" { - PrintErrorMessageAndExit("Project ID is required when using machine identity") - } - - // get folders via machine identity - folders, err := GetFoldersViaMachineIdentity(params.UniversalAuthAccessToken, params.WorkspaceId, params.Environment, params.FoldersPath) - folderErr = err - foldersToReturn = folders - } - return foldersToReturn, folderErr -} - -func GetFoldersViaJTW(JTWToken string, workspaceId string, environmentName string, foldersPath string) ([]models.SingleFolder, error) { - // set up resty client - httpClient, err := GetRestyClientWithCustomHeaders() - if err != nil { - return nil, err - } - - httpClient.SetAuthToken(JTWToken). - SetHeader("Accept", "application/json") - - getFoldersRequest := api.GetFoldersV1Request{ - WorkspaceId: workspaceId, - Environment: environmentName, - FoldersPath: foldersPath, - } - - apiResponse, err := api.CallGetFoldersV1(httpClient, getFoldersRequest) - if err != nil { - return nil, err - } - - var folders []models.SingleFolder - - for _, folder := range apiResponse.Folders { - folders = append(folders, models.SingleFolder{ - Name: folder.Name, - ID: folder.ID, - }) - } - - return folders, nil -} - -func GetFoldersViaServiceToken(fullServiceToken string, workspaceId string, environmentName string, foldersPath string) ([]models.SingleFolder, error) { - serviceTokenParts := strings.SplitN(fullServiceToken, ".", 4) - if len(serviceTokenParts) < 4 { - return nil, fmt.Errorf("invalid service token entered. Please double check your service token and try again") - } - - serviceToken := fmt.Sprintf("%v.%v.%v", serviceTokenParts[0], serviceTokenParts[1], serviceTokenParts[2]) - - httpClient, err := GetRestyClientWithCustomHeaders() - if err != nil { - return nil, fmt.Errorf("unable to get client with custom headers [err=%v]", err) - } - - httpClient.SetAuthToken(serviceToken). - SetHeader("Accept", "application/json") - - serviceTokenDetails, err := api.CallGetServiceTokenDetailsV2(httpClient) - if err != nil { - return nil, fmt.Errorf("unable to get service token details. [err=%v]", err) - } - - // if multiple scopes are there then user needs to specify which environment and folder path - if environmentName == "" { - if len(serviceTokenDetails.Scopes) != 1 { - return nil, fmt.Errorf("you need to provide the --env for multiple environment scoped token") - } else { - environmentName = serviceTokenDetails.Scopes[0].Environment - } - } - - getFoldersRequest := api.GetFoldersV1Request{ - WorkspaceId: serviceTokenDetails.Workspace, - Environment: environmentName, - FoldersPath: foldersPath, - } - - apiResponse, err := api.CallGetFoldersV1(httpClient, getFoldersRequest) - if err != nil { - return nil, fmt.Errorf("unable to get folders. [err=%v]", err) - } - - var folders []models.SingleFolder - - for _, folder := range apiResponse.Folders { - folders = append(folders, models.SingleFolder{ - Name: folder.Name, - ID: folder.ID, - }) - } - - return folders, nil -} - -func GetFoldersViaMachineIdentity(accessToken string, workspaceId string, envSlug string, foldersPath string) ([]models.SingleFolder, error) { - httpClient, err := GetRestyClientWithCustomHeaders() - if err != nil { - return nil, err - } - - httpClient.SetAuthToken(accessToken). - SetHeader("Accept", "application/json") - - getFoldersRequest := api.GetFoldersV1Request{ - WorkspaceId: workspaceId, - Environment: envSlug, - FoldersPath: foldersPath, - } - - apiResponse, err := api.CallGetFoldersV1(httpClient, getFoldersRequest) - if err != nil { - return nil, err - } - - var folders []models.SingleFolder - - for _, folder := range apiResponse.Folders { - folders = append(folders, models.SingleFolder{ - Name: folder.Name, - ID: folder.ID, - }) - } - - return folders, nil -} - -// CreateFolder creates a folder in Infisical -func CreateFolder(params models.CreateFolderParameters) (models.SingleFolder, error) { - - // If no token is provided, we will try to get the token from the current logged in user - if params.InfisicalToken == "" { - RequireLogin() - loggedInUserDetails, err := GetCurrentLoggedInUserDetails(true) - - if err != nil { - return models.SingleFolder{}, err - } - - if loggedInUserDetails.LoginExpired { - loggedInUserDetails = EstablishUserLoginSession() - } - - params.InfisicalToken = loggedInUserDetails.UserCredentials.JTWToken - } - - // set up resty client - httpClient, err := GetRestyClientWithCustomHeaders() - if err != nil { - return models.SingleFolder{}, err - } - - httpClient.SetAuthToken(params.InfisicalToken). - SetHeader("Accept", "application/json"). - SetHeader("Content-Type", "application/json") - - createFolderRequest := api.CreateFolderV1Request{ - WorkspaceId: params.WorkspaceId, - Environment: params.Environment, - FolderName: params.FolderName, - Path: params.FolderPath, - } - - apiResponse, err := api.CallCreateFolderV1(httpClient, createFolderRequest) - if err != nil { - return models.SingleFolder{}, err - } - - folder := apiResponse.Folder - - return models.SingleFolder{ - Name: folder.Name, - ID: folder.ID, - }, nil -} - -func DeleteFolder(params models.DeleteFolderParameters) ([]models.SingleFolder, error) { - - // If no token is provided, we will try to get the token from the current logged in user - if params.InfisicalToken == "" { - RequireLogin() - - loggedInUserDetails, err := GetCurrentLoggedInUserDetails(true) - - if err != nil { - return nil, err - } - - if loggedInUserDetails.LoginExpired { - loggedInUserDetails = EstablishUserLoginSession() - } - - params.InfisicalToken = loggedInUserDetails.UserCredentials.JTWToken - } - - // set up resty client - httpClient, err := GetRestyClientWithCustomHeaders() - if err != nil { - return nil, err - } - - httpClient.SetAuthToken(params.InfisicalToken). - SetHeader("Accept", "application/json"). - SetHeader("Content-Type", "application/json") - - deleteFolderRequest := api.DeleteFolderV1Request{ - WorkspaceId: params.WorkspaceId, - Environment: params.Environment, - FolderName: params.FolderName, - Directory: params.FolderPath, - } - - apiResponse, err := api.CallDeleteFolderV1(httpClient, deleteFolderRequest) - if err != nil { - return nil, err - } - - var folders []models.SingleFolder - - for _, folder := range apiResponse.Folders { - folders = append(folders, models.SingleFolder{ - Name: folder.Name, - ID: folder.ID, - }) - } - - return folders, nil -} diff --git a/cli/packages/util/helper.go b/cli/packages/util/helper.go deleted file mode 100644 index abd9768aa..000000000 --- a/cli/packages/util/helper.go +++ /dev/null @@ -1,338 +0,0 @@ -package util - -import ( - "bytes" - "crypto/sha256" - "encoding/base64" - "encoding/hex" - "fmt" - "math/rand" - "os" - "os/exec" - "path" - "sort" - "strings" - "time" - - "github.com/Infisical/infisical-merge/packages/api" - "github.com/Infisical/infisical-merge/packages/models" - "github.com/spf13/cobra" -) - -type DecodedSymmetricEncryptionDetails = struct { - Cipher []byte - IV []byte - Tag []byte - Key []byte -} - -const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" - -func GetBase64DecodedSymmetricEncryptionDetails(key string, cipher string, IV string, tag string) (DecodedSymmetricEncryptionDetails, error) { - cipherx, err := base64.StdEncoding.DecodeString(cipher) - if err != nil { - return DecodedSymmetricEncryptionDetails{}, fmt.Errorf("Base64DecodeSymmetricEncryptionDetails: Unable to decode cipher text [err=%v]", err) - } - - keyx, err := base64.StdEncoding.DecodeString(key) - if err != nil { - return DecodedSymmetricEncryptionDetails{}, fmt.Errorf("Base64DecodeSymmetricEncryptionDetails: Unable to decode key [err=%v]", err) - } - - IVx, err := base64.StdEncoding.DecodeString(IV) - if err != nil { - return DecodedSymmetricEncryptionDetails{}, fmt.Errorf("Base64DecodeSymmetricEncryptionDetails: Unable to decode IV [err=%v]", err) - } - - tagx, err := base64.StdEncoding.DecodeString(tag) - if err != nil { - return DecodedSymmetricEncryptionDetails{}, fmt.Errorf("Base64DecodeSymmetricEncryptionDetails: Unable to decode tag [err=%v]", err) - } - - return DecodedSymmetricEncryptionDetails{ - Key: keyx, - Cipher: cipherx, - IV: IVx, - Tag: tagx, - }, nil -} - -// Helper function to sort the secrets by key so we can create a consistent output -func SortSecretsByKeys(secrets []models.SingleEnvironmentVariable) []models.SingleEnvironmentVariable { - sort.Slice(secrets, func(i, j int) bool { - return secrets[i].Key < secrets[j].Key - }) - return secrets -} - -func IsSecretEnvironmentValid(env string) bool { - if env == "prod" || env == "dev" || env == "test" || env == "staging" { - return true - } - return false -} - -func IsSecretTypeValid(s string) bool { - if s == "personal" || s == "shared" { - return true - } - return false -} - -func GetInfisicalToken(cmd *cobra.Command) (token *models.TokenDetails, err error) { - infisicalToken, err := cmd.Flags().GetString("token") - - if err != nil { - return nil, err - } - - var source = "--token flag" - - if infisicalToken == "" { // If no flag is passed, we first check for the universal auth access token env variable. - infisicalToken = os.Getenv(INFISICAL_UNIVERSAL_AUTH_ACCESS_TOKEN_NAME) - source = fmt.Sprintf("%s environment variable", INFISICAL_UNIVERSAL_AUTH_ACCESS_TOKEN_NAME) - - if infisicalToken == "" { // If it's still empty after the first env check, we check for the service token env variable. - infisicalToken = os.Getenv(INFISICAL_TOKEN_NAME) - source = fmt.Sprintf("%s environment variable", INFISICAL_TOKEN_NAME) - } - - if infisicalToken == "" { // if its still empty, check for the `TOKEN` environment variable (for gateway helm) - infisicalToken = os.Getenv(INFISICAL_GATEWAY_TOKEN_NAME_LEGACY) - source = fmt.Sprintf("%s environment variable", INFISICAL_GATEWAY_TOKEN_NAME_LEGACY) - } - } - - if infisicalToken == "" { // If it's empty, we return nothing at all. - return nil, nil - } - - if strings.HasPrefix(infisicalToken, "st.") { - return &models.TokenDetails{ - Type: SERVICE_TOKEN_IDENTIFIER, - Token: infisicalToken, - Source: source, - }, nil - } - - return &models.TokenDetails{ - Type: UNIVERSAL_AUTH_TOKEN_IDENTIFIER, - Token: infisicalToken, - Source: source, - }, nil - -} - -func UniversalAuthLogin(clientId string, clientSecret string) (api.UniversalAuthLoginResponse, error) { - httpClient, err := GetRestyClientWithCustomHeaders() - if err != nil { - return api.UniversalAuthLoginResponse{}, err - } - - httpClient.SetRetryCount(10000). - SetRetryMaxWaitTime(20 * time.Second). - SetRetryWaitTime(5 * time.Second) - - tokenResponse, err := api.CallUniversalAuthLogin(httpClient, api.UniversalAuthLoginRequest{ClientId: clientId, ClientSecret: clientSecret}) - if err != nil { - return api.UniversalAuthLoginResponse{}, err - } - - return tokenResponse, nil -} - -func RenewMachineIdentityAccessToken(accessToken string) (string, error) { - - httpClient, err := GetRestyClientWithCustomHeaders() - if err != nil { - return "", err - } - - httpClient.SetRetryCount(10000). - SetRetryMaxWaitTime(20 * time.Second). - SetRetryWaitTime(5 * time.Second) - - request := api.UniversalAuthRefreshRequest{ - AccessToken: accessToken, - } - - tokenResponse, err := api.CallMachineIdentityRefreshAccessToken(httpClient, request) - if err != nil { - return "", err - } - - return tokenResponse.AccessToken, nil -} - -// Checks if the passed in email already exists in the users slice -func ConfigContainsEmail(users []models.LoggedInUser, email string) bool { - for _, value := range users { - if value.Email == email { - return true - } - } - return false -} - -func RequireLogin() { - // get the config file that stores the current logged in user email - configFile, _ := GetConfigFile() - - if configFile.LoggedInUserEmail == "" { - EstablishUserLoginSession() - } -} - -func IsLoggedIn() bool { - configFile, _ := GetConfigFile() - return configFile.LoggedInUserEmail != "" -} - -func RequireServiceToken() { - serviceToken := os.Getenv(INFISICAL_TOKEN_NAME) - if serviceToken == "" { - PrintErrorMessageAndExit("No service token is found in your terminal") - } -} - -func RequireLocalWorkspaceFile() { - workspaceFilePath, _ := FindWorkspaceConfigFile() - if workspaceFilePath == "" { - PrintErrorMessageAndExit("It looks you have not yet connected this project to Infisical", "To do so, run [infisical init] then run your command again") - } - - workspaceFile, err := GetWorkSpaceFromFile() - if err != nil { - HandleError(err, "Unable to read your project configuration, please try initializing this project again.", "Run [infisical init]") - } - - if workspaceFile.WorkspaceId == "" { - PrintErrorMessageAndExit("Your project id is missing in your local config file. Please add it or run again [infisical init]") - } -} - -func ValidateWorkspaceFile(projectConfigFilePath string) { - workspaceFilePath, err := GetWorkSpaceFromFilePath(projectConfigFilePath) - if err != nil { - PrintErrorMessageAndExit(fmt.Sprintf("error reading your project config %v", err)) - } - - if workspaceFilePath.WorkspaceId == "" { - PrintErrorMessageAndExit("Your project id is missing in your local config file. Please add it or run again [infisical init]") - } -} - -func GetHashFromStringList(list []string) string { - hash := sha256.New() - - for _, item := range list { - hash.Write([]byte(item)) - } - - sum := sha256.Sum256(hash.Sum(nil)) - return fmt.Sprintf("%x", sum) -} - -// execCmd is a struct that holds the command and arguments to be executed. -// By using this struct, we can easily mock the command and arguments. -type execCmd struct { - cmd string - args []string -} - -var getCurrentBranchCmd = execCmd{ - cmd: "git", - args: []string{"symbolic-ref", "--short", "HEAD"}, -} - -func getCurrentBranch() (string, error) { - cmd := exec.Command(getCurrentBranchCmd.cmd, getCurrentBranchCmd.args...) - var out bytes.Buffer - cmd.Stdout = &out - err := cmd.Run() - if err != nil { - return "", err - } - return path.Base(strings.TrimSpace(out.String())), nil -} - -func AppendAPIEndpoint(address string) string { - // if it's empty return as it is - // Ensure the address does not already end with "/api" - if address == "" || strings.HasSuffix(address, "/api") { - return address - } - - // Check if the address ends with a slash and append accordingly - if address[len(address)-1] == '/' { - return address + "api" - } - return address + "/api" -} - -func ReadFileAsString(filePath string) (string, error) { - fileBytes, err := os.ReadFile(filePath) - - if err != nil { - return "", err - } - - return string(fileBytes), nil - -} - -func GetEnvVarOrFileContent(envName string, filePath string) (string, error) { - // First check if the environment variable is set - if envVarValue := os.Getenv(envName); envVarValue != "" { - return envVarValue, nil - } - - // If it's not set, try to read the file - fileContent, err := ReadFileAsString(filePath) - - if err != nil { - return "", fmt.Errorf("unable to read file content from file path '%s' [err=%v]", filePath, err) - } - - return fileContent, nil -} - -func GetCmdFlagOrEnv(cmd *cobra.Command, flag string, envNames []string) (string, error) { - value, flagsErr := cmd.Flags().GetString(flag) - if flagsErr != nil { - return "", flagsErr - } - if value == "" { - for _, env := range envNames { - value = strings.TrimSpace(os.Getenv(env)) - if value != "" { - break - } - } - } - if value == "" { - return "", fmt.Errorf("please provide %s flag", flag) - } - return value, nil -} - -func GenerateRandomString(length int) string { - b := make([]byte, length) - for i := range b { - b[i] = charset[rand.Intn(len(charset))] - } - return string(b) -} - -func GenerateETagFromSecrets(secrets []models.SingleEnvironmentVariable) string { - sortedSecrets := SortSecretsByKeys(secrets) - content := []byte{} - - for _, secret := range sortedSecrets { - content = append(content, []byte(secret.Key)...) - content = append(content, []byte(secret.Value)...) - } - - hash := sha256.Sum256(content) - return fmt.Sprintf(`"%s"`, hex.EncodeToString(hash[:])) -} diff --git a/cli/packages/util/init.go b/cli/packages/util/init.go deleted file mode 100644 index 4aecb2ab3..000000000 --- a/cli/packages/util/init.go +++ /dev/null @@ -1,46 +0,0 @@ -package util - -import ( - "fmt" - - "github.com/Infisical/infisical-merge/packages/api" - "github.com/Infisical/infisical-merge/packages/config" - "github.com/Infisical/infisical-merge/packages/models" -) - -func GetOrganizationsNameList(organizationResponse api.GetOrganizationsResponse) []string { - organizations := organizationResponse.Organizations - - if len(organizations) == 0 { - message := fmt.Sprintf("You don't have any organization created in Infisical. You must first create a organization at %s", config.INFISICAL_URL) - PrintErrorMessageAndExit(message) - } - - var organizationNames []string - for _, workspace := range organizations { - organizationNames = append(organizationNames, workspace.Name) - } - - return organizationNames -} - -func GetWorkspacesInOrganization(workspaceResponse api.GetWorkSpacesResponse, orgId string) ([]models.Workspace, []string) { - workspaces := workspaceResponse.Workspaces - - var filteredWorkspaces []models.Workspace - var workspaceNames []string - - for _, workspace := range workspaces { - if workspace.OrganizationId == orgId { - filteredWorkspaces = append(filteredWorkspaces, workspace) - workspaceNames = append(workspaceNames, workspace.Name) - } - } - - if len(filteredWorkspaces) == 0 { - message := fmt.Sprintf("You don't have any projects created in Infisical organization. You must first create a project at %s", config.INFISICAL_URL) - PrintErrorMessageAndExit(message) - } - - return filteredWorkspaces, workspaceNames -} diff --git a/cli/packages/util/keyringwrapper.go b/cli/packages/util/keyringwrapper.go deleted file mode 100644 index 9c8211a3c..000000000 --- a/cli/packages/util/keyringwrapper.go +++ /dev/null @@ -1,69 +0,0 @@ -package util - -import ( - "encoding/base64" - "fmt" - - "github.com/rs/zerolog/log" - "github.com/zalando/go-keyring" -) - -const MAIN_KEYRING_SERVICE = "infisical-cli" - -type TimeoutError struct { - message string -} - -func (e *TimeoutError) Error() string { - return e.message -} - -func SetValueInKeyring(key, value string) error { - currentVaultBackend, err := GetCurrentVaultBackend() - if err != nil { - PrintErrorAndExit(1, err, "Unable to get current vault. Tip: run [infisical rest] then try again") - } - - err = keyring.Set(currentVaultBackend, MAIN_KEYRING_SERVICE, key, value) - - if err != nil { - log.Debug().Msg(fmt.Sprintf("Error while setting default keyring: %v", err)) - configFile, _ := GetConfigFile() - - if configFile.VaultBackendPassphrase == "" { - encodedPassphrase := base64.StdEncoding.EncodeToString([]byte(GenerateRandomString(10))) // generate random passphrase - configFile.VaultBackendPassphrase = encodedPassphrase - configFile.VaultBackendType = VAULT_BACKEND_FILE_MODE - err = WriteConfigFile(&configFile) - if err != nil { - return err - } - - // We call this function at last to trigger the environment variable to be set - GetConfigFile() - } - - err = keyring.Set(VAULT_BACKEND_FILE_MODE, MAIN_KEYRING_SERVICE, key, value) - log.Debug().Msg(fmt.Sprintf("Error while setting file keyring: %v", err)) - } - - return err -} - -func GetValueInKeyring(key string) (string, error) { - currentVaultBackend, err := GetCurrentVaultBackend() - if err != nil { - PrintErrorAndExit(1, err, "Unable to get current vault. Tip: run [infisical reset] then try again") - } - return keyring.Get(currentVaultBackend, MAIN_KEYRING_SERVICE, key) - -} - -func DeleteValueInKeyring(key string) error { - currentVaultBackend, err := GetCurrentVaultBackend() - if err != nil { - return err - } - - return keyring.Delete(currentVaultBackend, MAIN_KEYRING_SERVICE, key) -} diff --git a/cli/packages/util/log.go b/cli/packages/util/log.go deleted file mode 100644 index 9e6e558ea..000000000 --- a/cli/packages/util/log.go +++ /dev/null @@ -1,49 +0,0 @@ -package util - -import ( - "fmt" - "os" - - "github.com/fatih/color" -) - -func HandleError(err error, messages ...string) { - PrintErrorAndExit(1, err, messages...) -} - -func PrintErrorAndExit(exitCode int, err error, messages ...string) { - printError(err) - - if len(messages) > 0 { - for _, message := range messages { - fmt.Println(message) - } - } - - supportMsg := fmt.Sprintf("\n\nIf this issue continues, get support at https://infisical.com/slack") - fmt.Fprintln(os.Stderr, supportMsg) - - os.Exit(exitCode) -} - -func PrintWarning(message string) { - color.New(color.FgYellow).Fprintf(os.Stderr, "Warning: %v \n", message) -} - -func PrintSuccessMessage(message string) { - color.New(color.FgGreen).Println(message) -} - -func PrintErrorMessageAndExit(messages ...string) { - if len(messages) > 0 { - for _, message := range messages { - fmt.Fprintln(os.Stderr, message) - } - } - - os.Exit(1) -} - -func printError(e error) { - color.New(color.FgRed).Fprintf(os.Stderr, "error: %v\n", e) -} diff --git a/cli/packages/util/secrets.go b/cli/packages/util/secrets.go deleted file mode 100644 index 814e7da23..000000000 --- a/cli/packages/util/secrets.go +++ /dev/null @@ -1,824 +0,0 @@ -package util - -import ( - "crypto/rand" - "encoding/base64" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "os" - "strings" - "unicode" - - "github.com/Infisical/infisical-merge/packages/api" - "github.com/Infisical/infisical-merge/packages/crypto" - "github.com/Infisical/infisical-merge/packages/models" - "github.com/rs/zerolog/log" - "github.com/zalando/go-keyring" - "gopkg.in/yaml.v3" -) - -func GetPlainTextSecretsViaServiceToken(fullServiceToken string, environment string, secretPath string, includeImports bool, recursive bool, tagSlugs string, expandSecretReferences bool) ([]models.SingleEnvironmentVariable, error) { - serviceTokenParts := strings.SplitN(fullServiceToken, ".", 4) - if len(serviceTokenParts) < 4 { - return nil, fmt.Errorf("invalid service token entered. Please double check your service token and try again") - } - - serviceToken := fmt.Sprintf("%v.%v.%v", serviceTokenParts[0], serviceTokenParts[1], serviceTokenParts[2]) - - httpClient, err := GetRestyClientWithCustomHeaders() - if err != nil { - return nil, fmt.Errorf("unable to get client with custom headers [err=%v]", err) - } - - httpClient.SetAuthToken(serviceToken). - SetHeader("Accept", "application/json") - - serviceTokenDetails, err := api.CallGetServiceTokenDetailsV2(httpClient) - if err != nil { - return nil, fmt.Errorf("unable to get service token details. [err=%v]", err) - } - - // if multiple scopes are there then user needs to specify which environment and secret path - if environment == "" { - if len(serviceTokenDetails.Scopes) != 1 { - return nil, fmt.Errorf("you need to provide the --env for multiple environment scoped token") - } else { - environment = serviceTokenDetails.Scopes[0].Environment - } - } - - rawSecrets, err := api.CallGetRawSecretsV3(httpClient, api.GetRawSecretsV3Request{ - WorkspaceId: serviceTokenDetails.Workspace, - Environment: environment, - SecretPath: secretPath, - IncludeImport: includeImports, - Recursive: recursive, - TagSlugs: tagSlugs, - ExpandSecretReferences: expandSecretReferences, - }) - - if err != nil { - return nil, err - } - - plainTextSecrets := []models.SingleEnvironmentVariable{} - - for _, secret := range rawSecrets.Secrets { - plainTextSecrets = append(plainTextSecrets, models.SingleEnvironmentVariable{Key: secret.SecretKey, Value: secret.SecretValue, Type: secret.Type, WorkspaceId: secret.Workspace}) - } - - if includeImports { - plainTextSecrets, err = InjectRawImportedSecret(plainTextSecrets, rawSecrets.Imports) - if err != nil { - return nil, err - } - } - - return plainTextSecrets, nil - -} - -func GetPlainTextSecretsV3(accessToken string, workspaceId string, environmentName string, secretsPath string, includeImports bool, recursive bool, tagSlugs string, expandSecretReferences bool) (models.PlaintextSecretResult, error) { - httpClient, err := GetRestyClientWithCustomHeaders() - if err != nil { - return models.PlaintextSecretResult{}, err - } - - httpClient.SetAuthToken(accessToken). - SetHeader("Accept", "application/json") - - getSecretsRequest := api.GetRawSecretsV3Request{ - WorkspaceId: workspaceId, - Environment: environmentName, - IncludeImport: includeImports, - Recursive: recursive, - TagSlugs: tagSlugs, - ExpandSecretReferences: expandSecretReferences, - } - - if secretsPath != "" { - getSecretsRequest.SecretPath = secretsPath - } - - rawSecrets, err := api.CallGetRawSecretsV3(httpClient, getSecretsRequest) - - if err != nil { - return models.PlaintextSecretResult{}, err - } - - plainTextSecrets := []models.SingleEnvironmentVariable{} - - for _, secret := range rawSecrets.Secrets { - plainTextSecrets = append(plainTextSecrets, models.SingleEnvironmentVariable{Key: secret.SecretKey, Value: secret.SecretValue, Type: secret.Type, WorkspaceId: secret.Workspace, SecretPath: secret.SecretPath}) - } - - if includeImports { - plainTextSecrets, err = InjectRawImportedSecret(plainTextSecrets, rawSecrets.Imports) - if err != nil { - return models.PlaintextSecretResult{}, err - } - } - - return models.PlaintextSecretResult{ - Secrets: plainTextSecrets, - Etag: rawSecrets.ETag, - }, nil -} - -func GetSinglePlainTextSecretByNameV3(accessToken string, workspaceId string, environmentName string, secretsPath string, secretName string) (models.SingleEnvironmentVariable, string, error) { - httpClient, err := GetRestyClientWithCustomHeaders() - if err != nil { - return models.SingleEnvironmentVariable{}, "", err - } - - httpClient.SetAuthToken(accessToken). - SetHeader("Accept", "application/json") - - getSecretsRequest := api.GetRawSecretV3ByNameRequest{ - WorkspaceID: workspaceId, - Environment: environmentName, - SecretName: secretName, - SecretPath: secretsPath, - } - - rawSecret, err := api.CallFetchSingleSecretByName(httpClient, getSecretsRequest) - - if err != nil { - return models.SingleEnvironmentVariable{}, "", err - } - - formattedSecrets := models.SingleEnvironmentVariable{ - Key: rawSecret.Secret.SecretKey, - WorkspaceId: rawSecret.Secret.Workspace, - Value: rawSecret.Secret.SecretValue, - Type: rawSecret.Secret.Type, - ID: rawSecret.Secret.ID, - Comment: rawSecret.Secret.SecretComment, - SecretPath: rawSecret.Secret.SecretPath, - } - - return formattedSecrets, rawSecret.ETag, nil -} - -func CreateDynamicSecretLease(accessToken string, projectSlug string, environmentName string, secretsPath string, slug string, ttl string) (models.DynamicSecretLease, error) { - httpClient, err := GetRestyClientWithCustomHeaders() - if err != nil { - return models.DynamicSecretLease{}, err - } - - httpClient.SetAuthToken(accessToken). - SetHeader("Accept", "application/json") - - dynamicSecretRequest := api.CreateDynamicSecretLeaseV1Request{ - ProjectSlug: projectSlug, - Environment: environmentName, - SecretPath: secretsPath, - Slug: slug, - TTL: ttl, - } - - dynamicSecret, err := api.CallCreateDynamicSecretLeaseV1(httpClient, dynamicSecretRequest) - if err != nil { - return models.DynamicSecretLease{}, err - } - - return models.DynamicSecretLease{ - Lease: dynamicSecret.Lease, - Data: dynamicSecret.Data, - DynamicSecret: dynamicSecret.DynamicSecret, - }, nil -} - -func InjectRawImportedSecret(secrets []models.SingleEnvironmentVariable, importedSecrets []api.ImportedRawSecretV3) ([]models.SingleEnvironmentVariable, error) { - if importedSecrets == nil { - return secrets, nil - } - - hasOverriden := make(map[string]bool) - for _, sec := range secrets { - hasOverriden[sec.Key] = true - } - - for i := len(importedSecrets) - 1; i >= 0; i-- { - importSec := importedSecrets[i] - plainTextImportedSecrets := importSec.Secrets - - for _, sec := range plainTextImportedSecrets { - if _, ok := hasOverriden[sec.SecretKey]; !ok { - secrets = append(secrets, models.SingleEnvironmentVariable{ - Key: sec.SecretKey, - WorkspaceId: sec.Workspace, - Value: sec.SecretValue, - Type: sec.Type, - ID: sec.ID, - }) - hasOverriden[sec.SecretKey] = true - } - } - } - return secrets, nil -} - -func FilterSecretsByTag(plainTextSecrets []models.SingleEnvironmentVariable, tagSlugs string) []models.SingleEnvironmentVariable { - if tagSlugs == "" { - return plainTextSecrets - } - - tagSlugsMap := make(map[string]bool) - tagSlugsList := strings.Split(tagSlugs, ",") - for _, slug := range tagSlugsList { - tagSlugsMap[slug] = true - } - - filteredSecrets := []models.SingleEnvironmentVariable{} - for _, secret := range plainTextSecrets { - for _, tag := range secret.Tags { - if tagSlugsMap[tag.Slug] { - filteredSecrets = append(filteredSecrets, secret) - break - } - } - } - - return filteredSecrets -} - -func GetAllEnvironmentVariables(params models.GetAllSecretsParameters, projectConfigFilePath string) ([]models.SingleEnvironmentVariable, error) { - var secretsToReturn []models.SingleEnvironmentVariable - // var serviceTokenDetails api.GetServiceTokenDetailsResponse - var errorToReturn error - - if params.InfisicalToken == "" && params.UniversalAuthAccessToken == "" { - if params.WorkspaceId == "" { - if projectConfigFilePath == "" { - _, err := GetWorkSpaceFromFile() - if err != nil { - PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag") - } - } else { - ValidateWorkspaceFile(projectConfigFilePath) - } - } - - RequireLogin() - - log.Debug().Msg("GetAllEnvironmentVariables: Trying to fetch secrets using logged in details") - - loggedInUserDetails, err := GetCurrentLoggedInUserDetails(true) - isConnected := ValidateInfisicalAPIConnection() - - if isConnected { - log.Debug().Msg("GetAllEnvironmentVariables: Connected to Infisical instance, checking logged in creds") - } - - if err != nil { - return nil, err - } - - if isConnected && loggedInUserDetails.LoginExpired { - loggedInUserDetails = EstablishUserLoginSession() - } - - if params.WorkspaceId == "" { - var infisicalDotJson models.WorkspaceConfigFile - - if projectConfigFilePath == "" { - projectConfig, err := GetWorkSpaceFromFile() - if err != nil { - PrintErrorMessageAndExit("Please either run infisical init to connect to a project or pass in project id with --projectId flag") - } - - infisicalDotJson = projectConfig - } else { - projectConfig, err := GetWorkSpaceFromFilePath(projectConfigFilePath) - if err != nil { - return nil, err - } - - infisicalDotJson = projectConfig - } - params.WorkspaceId = infisicalDotJson.WorkspaceId - } - - res, err := GetPlainTextSecretsV3(loggedInUserDetails.UserCredentials.JTWToken, params.WorkspaceId, - params.Environment, params.SecretsPath, params.IncludeImport, params.Recursive, params.TagSlugs, true) - log.Debug().Msgf("GetAllEnvironmentVariables: Trying to fetch secrets JTW token [err=%s]", err) - - if err == nil { - backupEncryptionKey, err := GetBackupEncryptionKey() - if err != nil { - return nil, err - } - WriteBackupSecrets(params.WorkspaceId, params.Environment, params.SecretsPath, backupEncryptionKey, res.Secrets) - } - - secretsToReturn = res.Secrets - errorToReturn = err - // only attempt to serve cached secrets if no internet connection and if at least one secret cached - if !isConnected { - backupEncryptionKey, _ := GetBackupEncryptionKey() - if backupEncryptionKey != nil { - backedUpSecrets, err := ReadBackupSecrets(params.WorkspaceId, params.Environment, params.SecretsPath, backupEncryptionKey) - if len(backedUpSecrets) > 0 { - PrintWarning("Unable to fetch the latest secret(s) due to connection error, serving secrets from last successful fetch. For more info, run with --debug") - secretsToReturn = backedUpSecrets - errorToReturn = err - } - } - } - - } else { - if params.InfisicalToken != "" { - log.Debug().Msg("Trying to fetch secrets using service token") - secretsToReturn, errorToReturn = GetPlainTextSecretsViaServiceToken(params.InfisicalToken, params.Environment, params.SecretsPath, params.IncludeImport, params.Recursive, params.TagSlugs, params.ExpandSecretReferences) - } else if params.UniversalAuthAccessToken != "" { - - if params.WorkspaceId == "" { - PrintErrorMessageAndExit("Project ID is required when using machine identity") - } - - log.Debug().Msg("Trying to fetch secrets using universal auth") - res, err := GetPlainTextSecretsV3(params.UniversalAuthAccessToken, params.WorkspaceId, params.Environment, params.SecretsPath, params.IncludeImport, params.Recursive, params.TagSlugs, params.ExpandSecretReferences) - - errorToReturn = err - secretsToReturn = res.Secrets - } - } - - return secretsToReturn, errorToReturn -} - -func getSecretsByKeys(secrets []models.SingleEnvironmentVariable) map[string]models.SingleEnvironmentVariable { - secretMapByName := make(map[string]models.SingleEnvironmentVariable, len(secrets)) - - for _, secret := range secrets { - secretMapByName[secret.Key] = secret - } - - return secretMapByName -} - -func OverrideSecrets(secrets []models.SingleEnvironmentVariable, secretType string) []models.SingleEnvironmentVariable { - personalSecrets := make(map[string]models.SingleEnvironmentVariable) - sharedSecrets := make(map[string]models.SingleEnvironmentVariable) - secretsToReturn := []models.SingleEnvironmentVariable{} - secretsToReturnMap := make(map[string]models.SingleEnvironmentVariable) - - for _, secret := range secrets { - if secret.Type == PERSONAL_SECRET_TYPE_NAME { - personalSecrets[secret.Key] = secret - } - if secret.Type == SHARED_SECRET_TYPE_NAME { - sharedSecrets[secret.Key] = secret - } - } - - if secretType == PERSONAL_SECRET_TYPE_NAME { - for _, secret := range secrets { - if personalSecret, exists := personalSecrets[secret.Key]; exists { - secretsToReturnMap[secret.Key] = personalSecret - } else { - if _, exists = secretsToReturnMap[secret.Key]; !exists { - secretsToReturnMap[secret.Key] = secret - } - } - } - } else if secretType == SHARED_SECRET_TYPE_NAME { - for _, secret := range secrets { - if sharedSecret, exists := sharedSecrets[secret.Key]; exists { - secretsToReturnMap[secret.Key] = sharedSecret - } else { - if _, exists := secretsToReturnMap[secret.Key]; !exists { - secretsToReturnMap[secret.Key] = secret - } - } - } - } - - for _, secret := range secretsToReturnMap { - secretsToReturn = append(secretsToReturn, secret) - } - return secretsToReturn -} - -func GetBackupEncryptionKey() ([]byte, error) { - encryptionKey, err := GetValueInKeyring(INFISICAL_BACKUP_SECRET_ENCRYPTION_KEY) - if err != nil { - if err == keyring.ErrUnsupportedPlatform { - return nil, errors.New("your OS does not support keyring. Consider using a service token https://infisical.com/docs/documentation/platform/token") - } else if err == keyring.ErrNotFound { - // generate a new key - randomizedKey := make([]byte, 16) - rand.Read(randomizedKey) - encryptionKey = hex.EncodeToString(randomizedKey) - if err := SetValueInKeyring(INFISICAL_BACKUP_SECRET_ENCRYPTION_KEY, encryptionKey); err != nil { - return nil, err - } - return []byte(encryptionKey), nil - } else { - return nil, fmt.Errorf("something went wrong, failed to retrieve value from system keyring [error=%v]", err) - } - } - return []byte(encryptionKey), nil -} - -func WriteBackupSecrets(workspace string, environment string, secretsPath string, encryptionKey []byte, secrets []models.SingleEnvironmentVariable) error { - formattedPath := strings.ReplaceAll(secretsPath, "/", "-") - fileName := fmt.Sprintf("project_secrets_%s_%s_%s.json", workspace, environment, formattedPath) - secrets_backup_folder_name := "secrets-backup" - - _, fullConfigFileDirPath, err := GetFullConfigFilePath() - if err != nil { - return fmt.Errorf("WriteBackupSecrets: unable to get full config folder path [err=%s]", err) - } - - // create secrets backup directory - fullPathToSecretsBackupFolder := fmt.Sprintf("%s/%s", fullConfigFileDirPath, secrets_backup_folder_name) - if _, err := os.Stat(fullPathToSecretsBackupFolder); errors.Is(err, os.ErrNotExist) { - err := os.Mkdir(fullPathToSecretsBackupFolder, os.ModePerm) - if err != nil { - return err - } - } - marshaledSecrets, _ := json.Marshal(secrets) - result, err := crypto.EncryptSymmetric(marshaledSecrets, encryptionKey) - if err != nil { - return fmt.Errorf("WriteBackupSecrets: Unable to encrypt local secret backup to file [err=%s]", err) - } - listOfSecretsMarshalled, _ := json.Marshal(result) - err = os.WriteFile(fmt.Sprintf("%s/%s", fullPathToSecretsBackupFolder, fileName), listOfSecretsMarshalled, 0600) - if err != nil { - return fmt.Errorf("WriteBackupSecrets: Unable to write backup secrets to file [err=%s]", err) - } - - return nil -} - -func ReadBackupSecrets(workspace string, environment string, secretsPath string, encryptionKey []byte) ([]models.SingleEnvironmentVariable, error) { - formattedPath := strings.ReplaceAll(secretsPath, "/", "-") - fileName := fmt.Sprintf("project_secrets_%s_%s_%s.json", workspace, environment, formattedPath) - secrets_backup_folder_name := "secrets-backup" - - _, fullConfigFileDirPath, err := GetFullConfigFilePath() - if err != nil { - return nil, fmt.Errorf("ReadBackupSecrets: unable to write config file because an error occurred when getting config file path [err=%s]", err) - } - - fullPathToSecretsBackupFolder := fmt.Sprintf("%s/%s", fullConfigFileDirPath, secrets_backup_folder_name) - if _, err := os.Stat(fullPathToSecretsBackupFolder); errors.Is(err, os.ErrNotExist) { - return nil, nil - } - - encryptedBackupSecretsFilePath := fmt.Sprintf("%s/%s", fullPathToSecretsBackupFolder, fileName) - - encryptedBackupSecretsAsBytes, err := os.ReadFile(encryptedBackupSecretsFilePath) - if err != nil { - return nil, err - } - - var encryptedBackUpSecrets models.SymmetricEncryptionResult - err = json.Unmarshal(encryptedBackupSecretsAsBytes, &encryptedBackUpSecrets) - if err != nil { - return nil, fmt.Errorf("ReadBackupSecrets: unable to parse encrypted backup secrets. The secrets backup may be malformed [err=%s]", err) - } - - result, err := crypto.DecryptSymmetric(encryptionKey, encryptedBackUpSecrets.CipherText, encryptedBackUpSecrets.AuthTag, encryptedBackUpSecrets.Nonce) - if err != nil { - return nil, fmt.Errorf("ReadBackupSecrets: unable to decrypt encrypted backup secrets [err=%s]", err) - } - var plainTextSecrets []models.SingleEnvironmentVariable - _ = json.Unmarshal(result, &plainTextSecrets) - - return plainTextSecrets, nil - -} - -func DeleteBackupSecrets() error { - secrets_backup_folder_name := "secrets-backup" - - _, fullConfigFileDirPath, err := GetFullConfigFilePath() - if err != nil { - return fmt.Errorf("ReadBackupSecrets: unable to write config file because an error occurred when getting config file path [err=%s]", err) - } - - fullPathToSecretsBackupFolder := fmt.Sprintf("%s/%s", fullConfigFileDirPath, secrets_backup_folder_name) - DeleteValueInKeyring(INFISICAL_BACKUP_SECRET) - DeleteValueInKeyring(INFISICAL_BACKUP_SECRET_ENCRYPTION_KEY) - - return os.RemoveAll(fullPathToSecretsBackupFolder) -} - -func GetEnvFromWorkspaceFile() string { - workspaceFile, err := GetWorkSpaceFromFile() - if err != nil { - log.Debug().Msgf("getEnvFromWorkspaceFile: [err=%s]", err) - return "" - } - - if env := GetEnvelopmentBasedOnGitBranch(workspaceFile); env != "" { - return env - } - - return workspaceFile.DefaultEnvironment -} - -func GetEnvelopmentBasedOnGitBranch(workspaceFile models.WorkspaceConfigFile) string { - branch, err := getCurrentBranch() - if err != nil { - log.Debug().Msgf("getEnvelopmentBasedOnGitBranch: [err=%s]", err) - } - - envBasedOnGitBranch, ok := workspaceFile.GitBranchToEnvironmentMapping[branch] - - log.Debug().Msgf("GetEnvelopmentBasedOnGitBranch: [envBasedOnGitBranch=%s] [ok=%t]", envBasedOnGitBranch, ok) - - if err == nil && ok { - return envBasedOnGitBranch - } else { - log.Debug().Msgf("getEnvelopmentBasedOnGitBranch: [err=%s]", err) - return "" - } -} - -func GetPlainTextWorkspaceKey(authenticationToken string, receiverPrivateKey string, workspaceId string) ([]byte, error) { - httpClient, err := GetRestyClientWithCustomHeaders() - if err != nil { - return nil, fmt.Errorf("GetPlainTextWorkspaceKey: unable to get client with custom headers [err=%v]", err) - } - - httpClient.SetAuthToken(authenticationToken). - SetHeader("Accept", "application/json") - - request := api.GetEncryptedWorkspaceKeyRequest{ - WorkspaceId: workspaceId, - } - - workspaceKeyResponse, err := api.CallGetEncryptedWorkspaceKey(httpClient, request) - if err != nil { - return nil, fmt.Errorf("GetPlainTextWorkspaceKey: unable to retrieve your encrypted workspace key. [err=%v]", err) - } - - encryptedWorkspaceKey, err := base64.StdEncoding.DecodeString(workspaceKeyResponse.EncryptedKey) - if err != nil { - return nil, fmt.Errorf("GetPlainTextWorkspaceKey: Unable to get bytes represented by the base64 for encryptedWorkspaceKey [err=%v]", err) - } - - encryptedWorkspaceKeySenderPublicKey, err := base64.StdEncoding.DecodeString(workspaceKeyResponse.Sender.PublicKey) - if err != nil { - return nil, fmt.Errorf("GetPlainTextWorkspaceKey: Unable to get bytes represented by the base64 for encryptedWorkspaceKeySenderPublicKey [err=%v]", err) - } - - encryptedWorkspaceKeyNonce, err := base64.StdEncoding.DecodeString(workspaceKeyResponse.Nonce) - if err != nil { - return nil, fmt.Errorf("GetPlainTextWorkspaceKey: Unable to get bytes represented by the base64 for encryptedWorkspaceKeyNonce [err=%v]", err) - } - - currentUsersPrivateKey, err := base64.StdEncoding.DecodeString(receiverPrivateKey) - if err != nil { - return nil, fmt.Errorf("GetPlainTextWorkspaceKey: Unable to get bytes represented by the base64 for currentUsersPrivateKey [err=%v]", err) - } - - if len(currentUsersPrivateKey) == 0 || len(encryptedWorkspaceKeySenderPublicKey) == 0 { - return nil, fmt.Errorf("GetPlainTextWorkspaceKey: Missing credentials for generating plainTextEncryptionKey") - } - - return crypto.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey), nil -} - -func parseSecrets(fileName string, content string) (map[string]string, error) { - secrets := make(map[string]string) - - if strings.HasSuffix(fileName, ".yaml") || strings.HasSuffix(fileName, ".yml") { - // Handle YAML secrets - var yamlData map[string]interface{} - if err := yaml.Unmarshal([]byte(content), &yamlData); err != nil { - return nil, fmt.Errorf("failed to parse YAML file: %v", err) - } - - for key, value := range yamlData { - if strValue, ok := value.(string); ok { - secrets[key] = strValue - } else { - return nil, fmt.Errorf("YAML secret '%s' must be a string", key) - } - } - } else { - // Handle .env files - lines := strings.Split(content, "\n") - - for _, line := range lines { - line = strings.TrimSpace(line) - - // Ignore empty lines and comments - if line == "" || strings.HasPrefix(line, "#") || strings.HasPrefix(line, "//") { - continue - } - - // Ensure it's a valid key=value pair - splitKeyValue := strings.SplitN(line, "=", 2) - if len(splitKeyValue) != 2 { - return nil, fmt.Errorf("invalid format, expected key=value in line: %s", line) - } - - key, value := strings.TrimSpace(splitKeyValue[0]), strings.TrimSpace(splitKeyValue[1]) - - // Handle quoted values - if (strings.HasPrefix(value, `"`) && strings.HasSuffix(value, `"`)) || - (strings.HasPrefix(value, `'`) && strings.HasSuffix(value, `'`)) { - value = value[1 : len(value)-1] // Remove surrounding quotes - } - - secrets[key] = value - } - } - - return secrets, nil -} - -func validateSecretKey(key string) error { - if key == "" { - return errors.New("secret keys cannot be empty") - } - if unicode.IsNumber(rune(key[0])) { - return fmt.Errorf("secret key '%s' cannot start with a number", key) - } - if strings.Contains(key, " ") { - return fmt.Errorf("secret key '%s' cannot contain spaces", key) - } - return nil -} - -func SetRawSecrets(secretArgs []string, secretType string, environmentName string, secretsPath string, projectId string, tokenDetails *models.TokenDetails, file string) ([]models.SecretSetOperation, error) { - if file != "" { - content, err := os.ReadFile(file) - if err != nil { - if errors.Is(err, os.ErrNotExist) { - PrintErrorMessageAndExit("File does not exist") - } - return nil, fmt.Errorf("unable to process file [err=%v]", err) - } - - parsedSecrets, err := parseSecrets(file, string(content)) - if err != nil { - PrintErrorMessageAndExit(fmt.Sprintf("error parsing secrets: %v", err)) - } - - // Step 2: Validate secrets - for key, value := range parsedSecrets { - if err := validateSecretKey(key); err != nil { - PrintErrorMessageAndExit(err.Error()) - } - if strings.TrimSpace(value) == "" { - PrintErrorMessageAndExit(fmt.Sprintf("Secret key '%s' has an empty value", key)) - } - secretArgs = append(secretArgs, fmt.Sprintf("%s=%s", key, value)) - } - - if len(secretArgs) == 0 { - PrintErrorMessageAndExit("no valid secrets found in the file") - } - } - - if tokenDetails == nil { - return nil, fmt.Errorf("unable to process set secret operations, token details are missing") - } - - getAllEnvironmentVariablesRequest := models.GetAllSecretsParameters{Environment: environmentName, SecretsPath: secretsPath, WorkspaceId: projectId} - if tokenDetails.Type == UNIVERSAL_AUTH_TOKEN_IDENTIFIER { - getAllEnvironmentVariablesRequest.UniversalAuthAccessToken = tokenDetails.Token - } - - if tokenDetails.Type == SERVICE_TOKEN_IDENTIFIER { - getAllEnvironmentVariablesRequest.InfisicalToken = tokenDetails.Token - } - - httpClient, err := GetRestyClientWithCustomHeaders() - if err != nil { - return nil, fmt.Errorf("unable to get client with custom headers [err=%v]", err) - } - httpClient.SetAuthToken(tokenDetails.Token) - httpClient.SetHeader("Accept", "application/json") - - // pull current secrets - secrets, err := GetAllEnvironmentVariables(getAllEnvironmentVariablesRequest, "") - if err != nil { - return nil, fmt.Errorf("unable to retrieve secrets [err=%v]", err) - } - - secretsToCreate := []api.RawSecret{} - secretsToModify := []api.RawSecret{} - secretOperations := []models.SecretSetOperation{} - - sharedSecretMapByName := make(map[string]models.SingleEnvironmentVariable, len(secrets)) - personalSecretMapByName := make(map[string]models.SingleEnvironmentVariable, len(secrets)) - - for _, secret := range secrets { - if secret.Type == SECRET_TYPE_PERSONAL { - personalSecretMapByName[secret.Key] = secret - } else { - sharedSecretMapByName[secret.Key] = secret - } - } - - for _, arg := range secretArgs { - splitKeyValueFromArg := strings.SplitN(arg, "=", 2) - if splitKeyValueFromArg[0] == "" || splitKeyValueFromArg[1] == "" { - PrintErrorMessageAndExit("ensure that each secret has a none empty key and value. Modify the input and try again") - } - - if unicode.IsNumber(rune(splitKeyValueFromArg[0][0])) { - PrintErrorMessageAndExit("keys of secrets cannot start with a number. Modify the key name(s) and try again") - } - - // Key and value from argument - key := splitKeyValueFromArg[0] - value := splitKeyValueFromArg[1] - - var existingSecret models.SingleEnvironmentVariable - var doesSecretExist bool - - if secretType == SECRET_TYPE_SHARED { - existingSecret, doesSecretExist = sharedSecretMapByName[key] - } else { - existingSecret, doesSecretExist = personalSecretMapByName[key] - } - - if doesSecretExist { - // case: secret exists in project so it needs to be modified - encryptedSecretDetails := api.RawSecret{ - ID: existingSecret.ID, - SecretValue: value, - SecretKey: key, - Type: existingSecret.Type, - } - - // Only add to modifications if the value is different - if existingSecret.Value != value { - secretsToModify = append(secretsToModify, encryptedSecretDetails) - secretOperations = append(secretOperations, models.SecretSetOperation{ - SecretKey: key, - SecretValue: value, - SecretOperation: "SECRET VALUE MODIFIED", - }) - } else { - // Current value is same as existing so no change - secretOperations = append(secretOperations, models.SecretSetOperation{ - SecretKey: key, - SecretValue: value, - SecretOperation: "SECRET VALUE UNCHANGED", - }) - } - - } else { - // case: secret doesn't exist in project so it needs to be created - encryptedSecretDetails := api.RawSecret{ - SecretKey: key, - SecretValue: value, - Type: secretType, - } - secretsToCreate = append(secretsToCreate, encryptedSecretDetails) - secretOperations = append(secretOperations, models.SecretSetOperation{ - SecretKey: key, - SecretValue: value, - SecretOperation: "SECRET CREATED", - }) - } - } - - for _, secret := range secretsToCreate { - createSecretRequest := api.CreateRawSecretV3Request{ - SecretName: secret.SecretKey, - SecretValue: secret.SecretValue, - Type: secret.Type, - SecretPath: secretsPath, - WorkspaceID: projectId, - Environment: environmentName, - } - - err = api.CallCreateRawSecretsV3(httpClient, createSecretRequest) - if err != nil { - return nil, fmt.Errorf("unable to process new secret creations [err=%v]", err) - } - } - - for _, secret := range secretsToModify { - updateSecretRequest := api.UpdateRawSecretByNameV3Request{ - SecretName: secret.SecretKey, - SecretValue: secret.SecretValue, - SecretPath: secretsPath, - WorkspaceID: projectId, - Environment: environmentName, - Type: secret.Type, - } - - err = api.CallUpdateRawSecretsV3(httpClient, updateSecretRequest) - if err != nil { - return nil, fmt.Errorf("unable to process secret update request [err=%v]", err) - } - } - - return secretOperations, nil - -} diff --git a/cli/packages/util/testdata/infisical-branch-env.json b/cli/packages/util/testdata/infisical-branch-env.json deleted file mode 100644 index 657d68cd9..000000000 --- a/cli/packages/util/testdata/infisical-branch-env.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "workspaceId": "12345678", - "defaultEnvironment": "myDefaultEnv", - "gitBranchToEnvironmentMapping": { - "main": "myMainEnv" - } -} \ No newline at end of file diff --git a/cli/packages/util/testdata/infisical-default-env.json b/cli/packages/util/testdata/infisical-default-env.json deleted file mode 100644 index 63d37ad87..000000000 --- a/cli/packages/util/testdata/infisical-default-env.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "workspaceId": "12345678", - "defaultEnvironment": "myDefaultEnv", - "gitBranchToEnvironmentMapping": null -} \ No newline at end of file diff --git a/cli/packages/util/testdata/infisical-no-matching-branch-env.json b/cli/packages/util/testdata/infisical-no-matching-branch-env.json deleted file mode 100644 index 101dc61d6..000000000 --- a/cli/packages/util/testdata/infisical-no-matching-branch-env.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "workspaceId": "12345678", - "defaultEnvironment": "myDefaultEnv", - "gitBranchToEnvironmentMapping": { - "notmain": "myMainEnv" - } -} \ No newline at end of file diff --git a/cli/packages/util/vault.go b/cli/packages/util/vault.go deleted file mode 100644 index 5907d93fc..000000000 --- a/cli/packages/util/vault.go +++ /dev/null @@ -1,22 +0,0 @@ -package util - -import ( - "fmt" -) - -func GetCurrentVaultBackend() (string, error) { - configFile, err := GetConfigFile() - if err != nil { - return "", fmt.Errorf("getCurrentVaultBackend: unable to get config file [err=%s]", err) - } - - if configFile.VaultBackendType == "" { - return VAULT_BACKEND_AUTO_MODE, nil - } - - if configFile.VaultBackendType != VAULT_BACKEND_AUTO_MODE && configFile.VaultBackendType != VAULT_BACKEND_FILE_MODE { - return VAULT_BACKEND_AUTO_MODE, nil - } - - return configFile.VaultBackendType, nil -} diff --git a/cli/packages/visualize/dynamic_secret_leases.go b/cli/packages/visualize/dynamic_secret_leases.go deleted file mode 100644 index dbb588624..000000000 --- a/cli/packages/visualize/dynamic_secret_leases.go +++ /dev/null @@ -1,39 +0,0 @@ -package visualize - -import infisicalModels "github.com/infisical/go-sdk/packages/models" - -func PrintAllDyamicSecretLeaseCredentials(leaseCredentials map[string]any) { - rows := [][]string{} - for key, value := range leaseCredentials { - if cred, ok := value.(string); ok { - rows = append(rows, []string{key, cred}) - } - } - - headers := []string{"Key", "Value"} - - GenericTable(headers, rows) -} - -func PrintAllDynamicRootCredentials(dynamicRootCredentials []infisicalModels.DynamicSecret) { - rows := [][]string{} - for _, el := range dynamicRootCredentials { - rows = append(rows, []string{el.Name, el.Type, el.DefaultTTL, el.MaxTTL}) - } - - headers := []string{"Name", "Provider", "Default TTL", "Max TTL"} - - GenericTable(headers, rows) -} - -func PrintAllDynamicSecretLeases(dynamicSecretLeases []infisicalModels.DynamicSecretLease) { - rows := [][]string{} - const timeformat = "02-Jan-2006 03:04:05 PM" - for _, el := range dynamicSecretLeases { - rows = append(rows, []string{el.Id, el.ExpireAt.Local().Format(timeformat), el.CreatedAt.Local().Format(timeformat)}) - } - - headers := []string{"ID", "Expire At", "Created At"} - - GenericTable(headers, rows) -} diff --git a/cli/packages/visualize/folders.go b/cli/packages/visualize/folders.go deleted file mode 100644 index 74f053c4f..000000000 --- a/cli/packages/visualize/folders.go +++ /dev/null @@ -1,14 +0,0 @@ -package visualize - -import "github.com/Infisical/infisical-merge/packages/models" - -func PrintAllFoldersDetails(folders []models.SingleFolder, path string) { - rows := [][3]string{} - for _, folder := range folders { - rows = append(rows, [...]string{folder.Name, path, folder.ID}) - } - - headers := [...]string{"FOLDER NAME", "PATH", "FOLDER ID"} - - Table(headers, rows) -} diff --git a/cli/packages/visualize/secrets.go b/cli/packages/visualize/secrets.go deleted file mode 100644 index 7be41020d..000000000 --- a/cli/packages/visualize/secrets.go +++ /dev/null @@ -1,14 +0,0 @@ -package visualize - -import "github.com/Infisical/infisical-merge/packages/models" - -func PrintAllSecretDetails(secrets []models.SingleEnvironmentVariable) { - rows := [][3]string{} - for _, secret := range secrets { - rows = append(rows, [...]string{secret.Key, secret.Value, secret.Type}) - } - - headers := [...]string{"SECRET NAME", "SECRET VALUE", "SECRET TYPE"} - - Table(headers, rows) -} diff --git a/cli/packages/visualize/visualize.go b/cli/packages/visualize/visualize.go deleted file mode 100644 index 7fbd24fb8..000000000 --- a/cli/packages/visualize/visualize.go +++ /dev/null @@ -1,134 +0,0 @@ -package visualize - -import ( - "os" - "strings" - - "github.com/jedib0t/go-pretty/table" - "github.com/mattn/go-isatty" - "github.com/muesli/ansi" - "github.com/muesli/reflow/truncate" - "github.com/rs/zerolog/log" - "golang.org/x/term" -) - -type TableOptions struct { - Title string -} - -// func GetDefaultTableOptions() TableOptions{ -// return TableOptions{ -// Title: "", -// } -// } - -const ( - // combined width of the table borders and padding - borderWidths = 10 - // char to indicate that a string has been truncated - ellipsis = "…" -) - -// Given headers and rows, this function will print out a table -func Table(headers [3]string, rows [][3]string) { - // if we're not in a terminal or cygwin terminal, don't truncate the secret value - shouldTruncate := isatty.IsTerminal(os.Stdout.Fd()) - - // This will return an error if we're not in a terminal or - // if the terminal is a cygwin terminal like Git Bash. - width, _, err := term.GetSize(int(os.Stdout.Fd())) - if err != nil { - if shouldTruncate { - log.Error().Msgf("error getting terminal size: %s", err) - } else { - log.Debug().Err(err) - } - } - - longestSecretName, longestSecretType := getLongestValues(append(rows, headers)) - availableWidth := width - longestSecretName - longestSecretType - borderWidths - if availableWidth < 0 { - availableWidth = 0 - } - - t := table.NewWriter() - t.SetOutputMirror(os.Stdout) - t.SetStyle(table.StyleLight) - - // t.SetTitle(tableOptions.Title) - t.Style().Options.DrawBorder = true - t.Style().Options.SeparateHeader = true - t.Style().Options.SeparateColumns = true - - tableHeaders := table.Row{} - for _, header := range headers { - tableHeaders = append(tableHeaders, header) - } - - t.AppendHeader(tableHeaders) - for _, row := range rows { - tableRow := table.Row{} - for i, val := range row { - // only truncate the first column (secret value) - if i == 1 && stringWidth(val) > availableWidth && shouldTruncate { - val = truncate.StringWithTail(val, uint(availableWidth), ellipsis) - } - tableRow = append(tableRow, val) - } - t.AppendRow(tableRow) - } - - t.Render() -} - -// getLongestValues returns the length of the longest secret name and type from all rows (including the header). -func getLongestValues(rows [][3]string) (longestSecretName, longestSecretType int) { - for _, row := range rows { - if len(row[0]) > longestSecretName { - longestSecretName = stringWidth(row[0]) - } - if len(row[2]) > longestSecretType { - longestSecretType = stringWidth(row[2]) - } - } - return -} - -func GenericTable(headers []string, rows [][]string) { - t := table.NewWriter() - t.SetOutputMirror(os.Stdout) - t.SetStyle(table.StyleLight) - - // t.SetTitle(tableOptions.Title) - t.Style().Options.DrawBorder = true - t.Style().Options.SeparateHeader = true - t.Style().Options.SeparateColumns = true - - tableHeaders := table.Row{} - for _, header := range headers { - tableHeaders = append(tableHeaders, header) - } - - t.AppendHeader(tableHeaders) - for _, row := range rows { - tableRow := table.Row{} - for _, val := range row { - tableRow = append(tableRow, val) - } - t.AppendRow(tableRow) - } - - t.Render() -} - -// stringWidth returns the width of a string. -// ANSI escape sequences are ignored and double-width characters are handled correctly. -func stringWidth(str string) (width int) { - for _, l := range strings.Split(str, "\n") { - w := ansi.PrintableRuneWidth(l) - if w > width { - width = w - } - } - return width -} diff --git a/cli/scripts/completions.sh b/cli/scripts/completions.sh deleted file mode 100755 index 6e69a1508..000000000 --- a/cli/scripts/completions.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh -set -e -rm -rf completions -mkdir completions -cd cli -for sh in bash zsh fish; do - go run . completion "$sh" > "../completions/infisical.$sh" -done \ No newline at end of file diff --git a/cli/scripts/export_test_env.sh b/cli/scripts/export_test_env.sh deleted file mode 100644 index 0b242281d..000000000 --- a/cli/scripts/export_test_env.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -TEST_ENV_FILE=".test.env" - -# Check if the .env file exists -if [ ! -f "$TEST_ENV_FILE" ]; then - echo "$TEST_ENV_FILE does not exist." - exit 1 -fi - -# Export the variables -while IFS= read -r line -do - # Skip empty lines and lines starting with # - if [[ -z "$line" || "$line" =~ ^\# ]]; then - continue - fi - # Read the key-value pair - IFS='=' read -r key value <<< "$line" - eval export $key=\$value -done < "$TEST_ENV_FILE" - -echo "Test environment variables set." diff --git a/cli/scripts/install.sh b/cli/scripts/install.sh deleted file mode 100755 index a6dd4a4ec..000000000 --- a/cli/scripts/install.sh +++ /dev/null @@ -1,97 +0,0 @@ -#!/bin/bash - -PLATFORM= -ARCH= -TEMP_DOWNLOAD_FOLDER= - -function delete_temp_install_folder() -{ - $(rm -rf $TEMP_DOWNLOAD_FOLDER 2> /dev/null) -} - -# platform -case "$(uname -s)" in - Linux) PLATFORM='linux';; - Darwin) PLATFORM='darwin';; - CYGWIN*|MINGW*|MSYS*) PLATFORM='windows';; - FreeBSD) PLATFORM='freebsd';; - *) - echo "Your platform doesn't seem to be of type darwin, linux or windows" - echo "Your architecture is $(uname -m) and your platform is $(uname -s)" - exit 1 - ;; -esac - -# architecture -if [[ "$(uname -m)" == 'x86_64' || "$(uname -m)" == "amd64" ]]; then - ARCH="amd64" -elif [[ "$(uname -m)" == armv5* ]]; then - ARCH="armv5" -elif [[ "$(uname -m)" == armv6* ]]; then - ARCH="armv6" -elif [[ "$(uname -m)" == armv7* ]]; then - ARCH="armv7" -elif [[ "$(uname -m)" == 'arm64' || "$(uname -m)" == 'aarch64' ]]; then - ARCH="arm64" -elif [[ "$(uname -m)" == "i386" || "$(uname -m)" == "i686" ]]; then - ARCH="i386" -else - echo >&2 "Your architecture doesn't seem to supported. Your architecture is $(uname -m) and your platform is $(uname -s)" - exit 1 -fi - -# Credit https://stackoverflow.com/questions/20010199/how-to-determine-if-a-process-runs-inside-lxc-docker -if [[ "$(cat /proc/1/cgroup 2> /dev/null | grep docker | wc -l)" > 0 ]] || [ -f /.dockerenv ]; then - IS_RUNNING_IN_DOCKER=true -else - IS_RUNNING_IN_DOCKER=false -fi - -# example: v0.0.98 -LATEST_RELEASE_VERSION=$(curl -s "https://api.github.com/repos/Infisical/infisical/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') - -# example: 0.0.98 -NUMERIC_RELEASE_VERSION="${LATEST_RELEASE_VERSION:1}" - -DOWNLOAD_LINK="https://github.com/Infisical/infisical/releases/download/${LATEST_RELEASE_VERSION}/infisical_${NUMERIC_RELEASE_VERSION}_${PLATFORM}_${ARCH}.tar.gz" - -CHECK_IF_BINARY_EXISTS=$(curl -s -o -L /dev/null -w "%{http_code}" ${DOWNLOAD_LINK}) -if [[ $CHECK_IF_BINARY_EXISTS == "000Not Found404" ]]; then - echo "Looks like we do not yet have a binary for this architecture and platform." - echo "Your architecture is $(uname -m) and your platform is $(uname -s)" - exit 1 -fi - -# make temp install folder -mkdir -p infisical_temp_download_folder - -cd infisical_temp_download_folder - -TEMP_DOWNLOAD_FOLDER=$(pwd) - -# download latest cli -curl -L -o infisical-binary.tar.gz ${DOWNLOAD_LINK} - -# open up the tar file -tar zxf infisical-binary.tar.gz - -if [ "$PLATFORM" == "darwin" ] || [ $RUNNING_IN_DOCKER ] ; then - if [[ -d /usr/local/bin ]]; then - mv infisical /usr/local/bin/ - echo "Infisical CLI ${LATEST_RELEASE_VERSION} has been installed in /usr/local/bin." - else - echo >&2 "Error: /usr/local/bin does not exist. You must create it before reinstalling" - delete_temp_install_folder - exit 1 - fi -elif [ "$PLATFORM" == "windows" ]; then - mkdir $HOME/bin 2> /dev/null - mv infisical.exe $HOME/bin/ - echo "Infisical CLI ${LATEST_RELEASE_VERSION} has been installed in $HOME/bin" - echo "Please add $HOME/bin to your system PATH" -else - sudo mv infisical /usr/local/bin/ - echo "Infisical CLI ${LATEST_RELEASE_VERSION} has been installed in /usr/local/bin." -fi - -delete_temp_install_folder \ No newline at end of file diff --git a/cli/scripts/manpages.sh b/cli/scripts/manpages.sh deleted file mode 100755 index db7e5c1b5..000000000 --- a/cli/scripts/manpages.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh -set -e -rm -rf manpages -mkdir manpages -cd cli -go run . man | gzip -c > "../manpages/infisical.1.gz" \ No newline at end of file diff --git a/cli/scripts/setup.deb.sh b/cli/scripts/setup.deb.sh deleted file mode 100644 index ef24bcadc..000000000 --- a/cli/scripts/setup.deb.sh +++ /dev/null @@ -1,551 +0,0 @@ -#!/usr/bin/env bash -# -# The core commands execute start from the "MAIN" section below. -# - -test -z "$BASH_SOURCE" && { - self="sudo -E bash" - prefix=" |" -} || { - self=$(readlink -f ${BASH_SOURCE:-$0}) - prefix="" -} - -tmp_log=$(mktemp .s3_setup_XXXXXXXXX) - -# Environment variables that can be set -PKG_URL=${PKG_URL:-"https://artifacts-cli.infisical.com"} -PKG_PATH=${PKG_PATH:-"deb"} -PACKAGE_NAME=${PACKAGE_NAME:-"infisical"} -GPG_KEY_URL=${GPG_KEY_URL:-"${PKG_URL}/infisical.gpg"} - -colours=$(tput colors 2>/dev/null || echo "256") -no_colour="\e[39;49m" -green_colour="\e[32m" -red_colour="\e[41;97m" -bold="\e[1m" -reset="\e[0m" -use_colours=$(test -n "$colours" && test $colours -ge 8 && echo "yes") -test "$use_colours" == "yes" || { - no_colour="" - green_colour="" - red_colour="" - bold="" - reset="" -} - -example_name="Ubuntu/Focal (20.04)" -example_distro="ubuntu" -example_codename="focal" -example_version="20.04" - -function echo_helptext { - local help_text="$*" - echo " ^^^^: ... $help_text" -} - -function die { - local text="$@" - test ! -z "$text" && { - echo_helptext "$text" 1>&2 - } - - local prefix="${red_colour} !!!!${no_colour}" - - echo -e "$prefix: Oh no, your setup failed! :-( ... But we might be able to help. :-)" - echo -e "$prefix: " - echo -e "$prefix: ${bold}Please check your S3 bucket configuration and try again.${reset}" - echo -e "$prefix: " - - test -f "$tmp_log" && { - local n=20 - echo -e "$prefix: Last $n log lines from $tmp_log (might not be errors, nor even relevant):" - echo -e "$prefix:" - check_tool_silent "xargs" && { - check_tool_silent "fmt" && { - tail -n $n $tmp_log | fmt -t | xargs -Ilog echo -e "$prefix: > log" - } || { - tail -n $n $tmp_log | xargs -Ilog echo -e "$prefix: > log" - } - } || { - echo - tail -n $n $tmp_log - } - } - exit 1 -} - -function echo_colour { - local colour="${1:-"no"}_colour"; shift - echo -e "${!colour}$@${no_colour}" -} - -function echo_green_or_red { - local rc="$1" - local good="${2:-YES}" - local bad="${3:-NO}" - - test "$rc" -eq 0 && { - echo_colour "green" "$good" - } || { - echo_colour "red" "$bad" - } - return $rc -} - -function echo_clearline { - local rc="$?" - echo -e -n "\033[1K\r" - return $rc -} - -function echo_status { - local rc="$1" - local good="$2" - local bad="$3" - local text="$4" - local help_text="$5" - local newline=$(test "$6" != "no" && echo "\n" || echo "") - local status_text=$(echo_green_or_red "$rc" "$good" "$bad") - - echo_clearline - local width=$(test "$use_colours" == "yes" && echo "16" || echo "5") - printf "%${width}s %s${newline}" "${status_text}:" "$text" - test $rc -ne 0 && test ! -z "$help_text" && { - echo_helptext "$help_text" - echo - } - - return $rc -} - -function echo_running { - local rc=$? - local text="$1" - echo_status 0 " RUN" " RUN" "$text" "" "no" - return $rc -} - -function echo_okfail_rc { - local rc=$1 - local text="$2" - local help_text="$3" - echo_clearline - echo_status $rc " OK" " NOPE" "$text" "$help_text" - return $rc -} - -function echo_okfail { - echo_okfail_rc $? "$@" - return $? -} - -function check_tool_silent { - local tool=${1} - command -v $tool &>/dev/null || which $tool &>/dev/null - return $? -} - -function check_tool { - local tool=${1} - local optional=${2:-false} - local required_text="optional" - if ! $optional; then required_text="required"; fi - local text="Checking for $required_text executable '$tool' ..." - echo_running "$text" - check_tool_silent "$tool" - echo_okfail "$text" || { - if ! $optional; then - die "$tool is not installed, but is required by this script." - fi - return 1 - } - return 0 -} - -function cleanup { - echo - rm -rf $tmp_log -} - -function shutdown { - echo_colour "red" " !!!!: Operation cancelled by user!" - exit 2 -} - -function check_os { - test ! -z "$distro" && test ! -z "${version}${codename}" - return $? -} - -function detect_os_system { - check_os && return 0 - echo_running "$text" - local text="Detecting your OS distribution and release using system methods ..." - - local tool_rc=1 - test -f '/etc/os-release' && { - . /etc/os-release - distro=${distro:-$ID} - codename=${codename:-$VERSION_CODENAME} - codename=${codename:-$(echo $VERSION | cut -d '(' -f 2 | cut -d ')' -f 1)} - version=${version:-$VERSION_ID} - - test -z "${version}${codename}" && test -f '/etc/debian_version' && { - # Workaround for Debian unstable releases; get the codename from debian_version - codename=$(cat /etc/debian_version | cut -d '/' -f1) - } - - tool_rc=0 - } - - check_os - local rc=$? - echo_okfail_rc $rc "$text" - - test $tool_rc -eq 0 && { - report_os_expanded - } - - return $rc -} - -function report_os_attribute { - local name=$1 - local value=$2 - local coloured="" - echo -n "$name=" - test -z "$value" && { - echo -e -n "${red_colour}${no_colour} " - } || { - echo -e -n "${green_colour}${value}${no_colour} " - } -} - -function report_os_expanded { - echo_helptext "Detected/provided for your OS/distribution, version and architecture:" - echo " >>>>:" - report_os_values -} - -function report_os_values { - echo -n " >>>>: ... " - report_os_attribute "distro" $distro - report_os_attribute "codename" "stable (fixed)" - report_os_attribute "arch" $arch - echo - echo " >>>>:" -} - -function detect_os_legacy_python { - check_os && return 0 - - local text="Detecting your OS distribution and release using legacy python ..." - echo_running "$text" - - IFS='' read -r -d '' script <<-'EOF' -from __future__ import unicode_literals, print_function -import platform; -info = platform.linux_distribution() or ('', '', ''); -for key, value in zip(('distro', 'version', 'codename'), info): - print("local guess_%s=\"%s\"\n" % (key, value.lower().replace(' ', ''))); -EOF - - local tool_rc=1 - check_tool_silent "python" && { - eval $(python -c "$script") - distro=${distro:-$guess_distro} - codename=${codename:-$guess_codename} - version=${version:-$guess_version} - tool_rc=$? - } - - check_os - local rc=$? - echo_okfail_rc $rc "$text" - - check_tool_silent "python" || { - echo_helptext "Python isn't available, so skipping detection method (hint: install python)" - } - - test $tool_rc -eq 0 && { - report_os - } - - return $rc -} - -function detect_os_modern_python { - check_os && return 0 - - check_tool_silent "python" && { - local text="Ensuring python-pip is installed ..." - echo_running "$text" - check_tool_silent "pip" - echo_okfail "$text" || { - local text="Checking if pip can be bootstrapped without get-pip ..." - echo_running "$text" - python -m ensurepip --default-pip &>$tmp_log - echo_okfail "$text" || { - local text="Installing pip via get-pip bootstrap ..." - echo_running "$text" - curl -1sLf https://bootstrap.pypa.io/get-pip.py 2>$tmp/log | python &>$tmp_log - echo_okfail "$text" || die "Failed to install pip!" - } - } - - local text="Installing 'distro' python library ..." - echo_running "$text" - python -c 'import distro' &>$tmp_log || python -m pip install distro &>$tmp_log - echo_okfail "$text" || die "Failed to install required 'distro' python library!" - } - - IFS='' read -r -d '' script <<-'EOF' -from __future__ import unicode_literals, print_function -import distro; -info = distro.linux_distribution(full_distribution_name=False) or ('', '', ''); -for key, value in zip(('distro', 'version', 'codename'), info): - print("local guess_%s=\"%s\"\n" % (key, value.lower().replace(' ', ''))); -EOF - - local text="Detecting your OS distribution and release using modern python ..." - echo_running "$text" - - local tool_rc=1 - check_tool_silent "python" && { - eval $(python -c "$script") - distro=${distro:-$guess_distro} - codename=${codename:-$guess_codename} - version=${version:-$guess_version} - tool_rc=$? - } - - check_os - local rc=$? - echo_okfail_rc $rc "$text" - - check_tool_silent "python" || { - echo_helptext "Python isn't available, so skipping detection method (hint: install python)" - } - - test $tool_rc -eq 0 && { - report_os_expanded - } - - return $rc -} - -function detect_os { - # Backwards compat for old distribution parameter names - distro=${distro:-$os} - - # Always use "stable" as the codename - codename="stable" - - arch=${arch:-$(arch || uname -m)} - - # Only detect OS if not manually specified - if [ -z "$distro" ]; then - detect_os_system || - detect_os_legacy_python || - detect_os_modern_python - fi - - # Always ensure we have a distro - (test -z "$distro") && { - echo_okfail_rc "1" "Unable to detect your OS distribution!" - cat <>>>: - >>>>: The 'distro' value is required. Without it, the install script - >>>>: cannot retrieve the correct configuration for this system. - >>>>: - >>>>: You can force this script to use a particular value by specifying distro - >>>>: via environment variable. E.g., to specify a distro - >>>>: such as $example_name, use the following: - >>>>: - >>>>: $prefix distro=$example_distro $self - >>>>: -EOF - die - } -} - -function create_repo_config { - if [ -z "$PKG_PATH" ]; then - repo_url="${PKG_URL}" - else - repo_url="${PKG_URL}/${PKG_PATH}" - fi - - # Create configuration with GPG key verification - local gpg_keyring_path="/usr/share/keyrings/${PACKAGE_NAME}-archive-keyring.gpg" - local apt_conf=$(cat <>>>: - >>>>: It looks like we can't access the GPG key at ${GPG_KEY_URL} - >>>>: -EOF - die - } -} - -function check_dpkg_tool { - local tool=${1} - local required=${2:-true} - local install=${3:-true} - - local text="Checking for apt dependency '$tool' ..." - echo_running "$text" - dpkg -l | grep "$tool\>" &>$tmp_log - echo_okfail "$text" || { - if $install; then - test "$apt_updated" == "yes" || update_apt - local text="Attempting to install '$tool' ..." - echo_running "$text" - apt-get install -y "$tool" &>$tmp_log - echo_okfail "$text" || { - if $required; then - die "Could not install '$tool', check your permissions, etc." - fi - } - else { - if $required; then - die "$tool is not installed, but is required by this script." - fi - } - fi - } - return 0 -} - -function update_apt { - local text="Updating apt repository metadata cache ..." - local tmp_log=$(mktemp .s3_deb_output_XXXXXXXXX.log) - echo_running "$text" - apt-get update &>$tmp_log - echo_okfail "$text" || { - echo_colour "red" "Failed to update via apt-get update" - cat $tmp_log - rm -rf $tmp_log - die "Failed to update via apt-get update - Context above (maybe no packages?)." - } - rm -rf $tmp_log - apt_updated="yes" -} - -function install_apt_prereqs { - # Debian-archive-keyring has to be installed for apt-transport-https. - test "${distro}" == "debian" && { - check_dpkg_tool "debian-keyring" - check_dpkg_tool "debian-archive-keyring" - } - - check_dpkg_tool "apt-transport-https" - check_dpkg_tool "ca-certificates" false - check_dpkg_tool "gnupg" -} - -function import_gpg_key { - local text="Importing '$PACKAGE_NAME' repository GPG key from S3 ..." - echo_running "$text" - - local gpg_keyring_path="/usr/share/keyrings/${PACKAGE_NAME}-archive-keyring.gpg" - - # Check if GPG key is accessible - check_gpg_key - - # Download and import GPG key - curl -1sLf "${GPG_KEY_URL}" | gpg --dearmor > $gpg_keyring_path - chmod 644 $gpg_keyring_path - - # Check for older apt versions that don't support signed-by - local signed_by_version="1.1" - local detected_version=$(dpkg -s apt | grep Version | cut -d' ' -f2) - - if [ "$(printf "%s\n" $detected_version $signed_by_version | sort -V | head -n 1)" != "$signed_by_version" ]; then - echo_helptext "Detected older apt version without signed-by support. Copying key to trusted.gpg.d." - cp ${gpg_keyring_path} /etc/apt/trusted.gpg.d/${PACKAGE_NAME}.gpg - chmod 644 /etc/apt/trusted.gpg.d/${PACKAGE_NAME}.gpg - fi - - echo_okfail "$text" || die "Could not import the GPG key for this repository" -} - -function setup_repository { - local repo_path="/etc/apt/sources.list.d/${PACKAGE_NAME}.list" - - local text="Installing '$PACKAGE_NAME' repository via apt ..." - echo_running "$text" - create_repo_config > "$repo_path" - chmod 644 $repo_path - echo_okfail "$text" || die "Could not install the repository, do you have permissions?" -} - -function usage () { - cat <] [message="Environment with slug 'invalid-env' in project with ID bef697d4-849b-4a75-b284-0922f87f8ba2 not found"] - - -If this issue continues, get support at https://infisical.com/slack diff --git a/cli/test/.snapshots/test-TestUserAuth_SecretsGetAll b/cli/test/.snapshots/test-TestUserAuth_SecretsGetAll deleted file mode 100644 index 260607e97..000000000 --- a/cli/test/.snapshots/test-TestUserAuth_SecretsGetAll +++ /dev/null @@ -1,7 +0,0 @@ -┌───────────────┬──────────────┬─────────────┐ -│ SECRET NAME │ SECRET VALUE │ SECRET TYPE │ -├───────────────┼──────────────┼─────────────┤ -│ TEST-SECRET-1 │ test-value-1 │ shared │ -│ TEST-SECRET-2 │ test-value-2 │ shared │ -│ TEST-SECRET-3 │ test-value-3 │ shared │ -└───────────────┴──────────────┴─────────────┘ diff --git a/cli/test/.snapshots/test-testUserAuth_SecretsGetAllWithoutConnection b/cli/test/.snapshots/test-testUserAuth_SecretsGetAllWithoutConnection deleted file mode 100644 index 2ca9d13ad..000000000 --- a/cli/test/.snapshots/test-testUserAuth_SecretsGetAllWithoutConnection +++ /dev/null @@ -1,8 +0,0 @@ -Warning: Unable to fetch the latest secret(s) due to connection error, serving secrets from last successful fetch. For more info, run with --debug -┌───────────────┬──────────────┬─────────────┐ -│ SECRET NAME │ SECRET VALUE │ SECRET TYPE │ -├───────────────┼──────────────┼─────────────┤ -│ TEST-SECRET-1 │ test-value-1 │ shared │ -│ TEST-SECRET-2 │ test-value-2 │ shared │ -│ TEST-SECRET-3 │ test-value-3 │ shared │ -└───────────────┴──────────────┴─────────────┘ diff --git a/cli/test/export_test.go b/cli/test/export_test.go deleted file mode 100644 index c44bf20af..000000000 --- a/cli/test/export_test.go +++ /dev/null @@ -1,66 +0,0 @@ -package tests - -import ( - "testing" - - "github.com/bradleyjkemp/cupaloy/v2" -) - -func TestUniversalAuth_ExportSecretsWithImports(t *testing.T) { - MachineIdentityLoginCmd(t) - - output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "export", "--token", creds.UAAccessToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--silent") - - if err != nil { - t.Fatalf("error running CLI command: %v", err) - } - - // Use cupaloy to snapshot test the output - err = cupaloy.Snapshot(output) - if err != nil { - t.Fatalf("snapshot failed: %v", err) - } -} - -func TestServiceToken_ExportSecretsWithImports(t *testing.T) { - output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "export", "--token", creds.ServiceToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--silent") - - if err != nil { - t.Fatalf("error running CLI command: %v", err) - } - - // Use cupaloy to snapshot test the output - err = cupaloy.Snapshot(output) - if err != nil { - t.Fatalf("snapshot failed: %v", err) - } -} - -func TestUniversalAuth_ExportSecretsWithoutImports(t *testing.T) { - MachineIdentityLoginCmd(t) - output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "export", "--token", creds.UAAccessToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--silent", "--include-imports=false") - - if err != nil { - t.Fatalf("error running CLI command: %v", err) - } - - // Use cupaloy to snapshot test the output - err = cupaloy.Snapshot(output) - if err != nil { - t.Fatalf("snapshot failed: %v", err) - } -} - -func TestServiceToken_ExportSecretsWithoutImports(t *testing.T) { - output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "export", "--token", creds.ServiceToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--silent", "--include-imports=false") - - if err != nil { - t.Fatalf("error running CLI command: %v", err) - } - - // Use cupaloy to snapshot test the output - err = cupaloy.Snapshot(output) - if err != nil { - t.Fatalf("snapshot failed: %v", err) - } -} diff --git a/cli/test/helper.go b/cli/test/helper.go deleted file mode 100644 index 74e56237a..000000000 --- a/cli/test/helper.go +++ /dev/null @@ -1,107 +0,0 @@ -package tests - -import ( - "encoding/json" - "fmt" - "log" - "os" - "os/exec" - "regexp" - "strings" -) - -const ( - CLI_NAME = "infisical-merge" -) - -var ( - FORMATTED_CLI_NAME = fmt.Sprintf("./%s", CLI_NAME) -) - -type Credentials struct { - ClientID string - ClientSecret string - UAAccessToken string - ServiceToken string - ProjectID string - EnvSlug string - UserEmail string - UserPassword string -} - -var creds = Credentials{ - UAAccessToken: "", - ClientID: os.Getenv("CLI_TESTS_UA_CLIENT_ID"), - ClientSecret: os.Getenv("CLI_TESTS_UA_CLIENT_SECRET"), - ServiceToken: os.Getenv("CLI_TESTS_SERVICE_TOKEN"), - ProjectID: os.Getenv("CLI_TESTS_PROJECT_ID"), - EnvSlug: os.Getenv("CLI_TESTS_ENV_SLUG"), - UserEmail: os.Getenv("CLI_TESTS_USER_EMAIL"), - UserPassword: os.Getenv("CLI_TESTS_USER_PASSWORD"), -} - -func ExecuteCliCommand(command string, args ...string) (string, error) { - cmd := exec.Command(command, args...) - output, err := cmd.CombinedOutput() - - if err != nil { - fmt.Println(fmt.Sprint(err) + ": " + FilterRequestID(strings.TrimSpace(string(output)))) - return FilterRequestID(strings.TrimSpace(string(output))), err - } - return FilterRequestID(strings.TrimSpace(string(output))), nil -} - -func SetupCli() { - - if creds.ClientID == "" || creds.ClientSecret == "" || creds.ServiceToken == "" || creds.ProjectID == "" || creds.EnvSlug == "" { - panic("Missing required environment variables") - } - - // check if the CLI is already built, if not build it - alreadyBuilt := false - if _, err := os.Stat(FORMATTED_CLI_NAME); err == nil { - alreadyBuilt = true - } - - if !alreadyBuilt { - if err := exec.Command("go", "build", "../.").Run(); err != nil { - log.Fatal(err) - } - } - -} - -func FilterRequestID(input string) string { - requestIDPattern := regexp.MustCompile(`\[request-id=[^\]]+\]`) - reqIDPattern := regexp.MustCompile(`\[reqId=[^\]]+\]`) - input = requestIDPattern.ReplaceAllString(input, "[request-id=]") - input = reqIDPattern.ReplaceAllString(input, "[reqId=]") - - start := strings.Index(input, "{") - end := strings.LastIndex(input, "}") + 1 - - if start == -1 || end == -1 { - return input - } - - jsonPart := input[:start] // Pre-JSON content - - // Parse the JSON object - var errorObj map[string]interface{} - if err := json.Unmarshal([]byte(input[start:end]), &errorObj); err != nil { - return input - } - - // Remove requestId field - delete(errorObj, "requestId") - delete(errorObj, "reqId") - - // Convert back to JSON - filtered, err := json.Marshal(errorObj) - if err != nil { - return input - } - - // Reconstruct the full string - return jsonPart + string(filtered) + input[end:] -} diff --git a/cli/test/login_test.go b/cli/test/login_test.go deleted file mode 100644 index 71273a3ec..000000000 --- a/cli/test/login_test.go +++ /dev/null @@ -1,139 +0,0 @@ -package tests - -import ( - "log" - "os/exec" - "strings" - "testing" - - "github.com/creack/pty" - "github.com/stretchr/testify/assert" -) - -func UserInitCmd() { - c := exec.Command(FORMATTED_CLI_NAME, "init") - ptmx, err := pty.Start(c) - if err != nil { - log.Fatalf("error running CLI command: %v", err) - } - defer func() { _ = ptmx.Close() }() - - stepChan := make(chan int, 10) - - go func() { - buf := make([]byte, 1024) - step := -1 - for { - n, err := ptmx.Read(buf) - if n > 0 { - terminalOut := string(buf) - if strings.Contains(terminalOut, "Which Infisical organization would you like to select a project from?") && step < 0 { - step += 1 - stepChan <- step - } else if strings.Contains(terminalOut, "Which of your Infisical projects would you like to connect this project to?") && step < 1 { - step += 1; - stepChan <- step - } - } - if err != nil { - close(stepChan) - return - } - } - }() - - for i := range stepChan { - switch i { - case 0: - ptmx.Write([]byte("\n")) - case 1: - ptmx.Write([]byte("\n")) - } - } -} - -func UserLoginCmd() { - // set vault to file because CI has no keyring - vaultCmd := exec.Command(FORMATTED_CLI_NAME, "vault", "set", "file") - _, err := vaultCmd.Output() - if err != nil { - log.Fatalf("error setting vault: %v", err) - } - - // Start programmatic interaction with CLI - c := exec.Command(FORMATTED_CLI_NAME, "login", "--interactive") - ptmx, err := pty.Start(c) - if err != nil { - log.Fatalf("error running CLI command: %v", err) - } - defer func() { _ = ptmx.Close() }() - - stepChan := make(chan int, 10) - - go func() { - buf := make([]byte, 1024) - step := -1 - for { - n, err := ptmx.Read(buf) - if n > 0 { - terminalOut := string(buf) - if strings.Contains(terminalOut, "Infisical Cloud") && step < 0 { - step += 1; - stepChan <- step - } else if strings.Contains(terminalOut, "Email") && step < 1 { - step += 1; - stepChan <- step - } else if strings.Contains(terminalOut, "Password") && step < 2 { - step += 1; - stepChan <- step - } else if strings.Contains(terminalOut, "Infisical organization") && step < 3 { - step += 1; - stepChan <- step - } else if strings.Contains(terminalOut, "Enter passphrase") && step < 4 { - step += 1; - stepChan <- step - } - } - if err != nil { - close(stepChan) - return - } - } - }() - - for i := range stepChan { - switch i { - case 0: - ptmx.Write([]byte("\n")) - case 1: - ptmx.Write([]byte(creds.UserEmail)) - ptmx.Write([]byte("\n")) - case 2: - ptmx.Write([]byte(creds.UserPassword)) - ptmx.Write([]byte("\n")) - case 3: - ptmx.Write([]byte("\n")) - } - } - -} - -func MachineIdentityLoginCmd(t *testing.T) { - if creds.UAAccessToken != "" { - return - } - - jwtPattern := `^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]*$` - - output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "login", "--method=universal-auth", "--client-id", creds.ClientID, "--client-secret", creds.ClientSecret, "--plain", "--silent") - - if err != nil { - t.Fatalf("error running CLI command: %v", err) - } - - assert.Regexp(t, jwtPattern, output) - - creds.UAAccessToken = output - - // We can't use snapshot testing here because the output will be different every time -} diff --git a/cli/test/main_test.go b/cli/test/main_test.go deleted file mode 100644 index e14893aec..000000000 --- a/cli/test/main_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package tests - -import ( - "fmt" - "os" - "testing" -) - -func TestMain(m *testing.M) { - // Setup - fmt.Println("Setting up CLI...") - SetupCli() - fmt.Println("Performing user login...") - UserLoginCmd() - fmt.Println("Performing infisical init...") - UserInitCmd() - - // Run the tests - code := m.Run() - - // Exit - os.Exit(code) -} diff --git a/cli/test/run_test.go b/cli/test/run_test.go deleted file mode 100644 index d2c6021cc..000000000 --- a/cli/test/run_test.go +++ /dev/null @@ -1,108 +0,0 @@ -package tests - -import ( - "bytes" - "testing" - - "github.com/bradleyjkemp/cupaloy/v2" -) - -func TestServiceToken_RunCmdRecursiveAndImports(t *testing.T) { - output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "run", "--token", creds.ServiceToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--recursive", "--silent", "--", "echo", "hello world") - - if err != nil { - t.Fatalf("error running CLI command: %v", err) - } - - output = string(bytes.Split([]byte(output), []byte("INF"))[1]) - - // Use cupaloy to snapshot test the output - err = cupaloy.Snapshot(output) - if err != nil { - t.Fatalf("snapshot failed: %v", err) - } -} -func TestServiceToken_RunCmdWithImports(t *testing.T) { - output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "run", "--token", creds.ServiceToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--silent", "--", "echo", "hello world") - - if err != nil { - t.Fatalf("error running CLI command: %v", err) - } - - output = string(bytes.Split([]byte(output), []byte("INF"))[1]) - - // Use cupaloy to snapshot test the output - err = cupaloy.Snapshot(output) - if err != nil { - t.Fatalf("snapshot failed: %v", err) - } -} - -func TestUniversalAuth_RunCmdRecursiveAndImports(t *testing.T) { - MachineIdentityLoginCmd(t) - output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "run", "--token", creds.UAAccessToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--recursive", "--silent", "--", "echo", "hello world") - - if err != nil { - t.Fatalf("error running CLI command: %v", err) - } - - output = string(bytes.Split([]byte(output), []byte("INF"))[1]) - - // Use cupaloy to snapshot test the output - err = cupaloy.Snapshot(output) - if err != nil { - t.Fatalf("snapshot failed: %v", err) - } -} - -func TestUniversalAuth_RunCmdWithImports(t *testing.T) { - MachineIdentityLoginCmd(t) - output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "run", "--token", creds.UAAccessToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--silent", "--", "echo", "hello world") - - if err != nil { - t.Fatalf("error running CLI command: %v", err) - } - - // remove the first few characters from the output because we don't care about the time, and it will change every time - output = string(bytes.Split([]byte(output), []byte("INF"))[1]) - - // Use cupaloy to snapshot test the output - err = cupaloy.Snapshot(output) - if err != nil { - t.Fatalf("snapshot failed: %v", err) - } -} - -func TestUniversalAuth_RunCmdWithoutImports(t *testing.T) { - MachineIdentityLoginCmd(t) - output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "run", "--token", creds.UAAccessToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--silent", "--include-imports=false", "--", "echo", "hello world") - - if err != nil { - t.Fatalf("error running CLI command: %v", err) - } - - output = string(bytes.Split([]byte(output), []byte("INF"))[1]) - - // Use cupaloy to snapshot test the output - err = cupaloy.Snapshot(output) - if err != nil { - t.Fatalf("snapshot failed: %v", err) - } -} - -func TestServiceToken_RunCmdWithoutImports(t *testing.T) { - output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "run", "--token", creds.ServiceToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--silent", "--include-imports=false", "--", "echo", "hello world") - - if err != nil { - t.Fatalf("error running CLI command: %v", err) - } - - // Remove everything before "INF" because it's not relevant to the test - output = string(bytes.Split([]byte(output), []byte("INF"))[1]) - - // Use cupaloy to snapshot test the output - err = cupaloy.Snapshot(output) - if err != nil { - t.Fatalf("snapshot failed: %v", err) - } -} diff --git a/cli/test/secrets_by_name_test.go b/cli/test/secrets_by_name_test.go deleted file mode 100644 index 26a8314bb..000000000 --- a/cli/test/secrets_by_name_test.go +++ /dev/null @@ -1,94 +0,0 @@ -package tests - -import ( - "testing" - - "github.com/bradleyjkemp/cupaloy/v2" -) - -func TestServiceToken_GetSecretsByNameRecursive(t *testing.T) { - output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "secrets", "get", "TEST-SECRET-1", "TEST-SECRET-2", "FOLDER-SECRET-1", "--token", creds.ServiceToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--recursive", "--silent") - - if err != nil { - t.Fatalf("error running CLI command: %v", err) - } - - // Use cupaloy to snapshot test the output - err = cupaloy.Snapshot(output) - if err != nil { - t.Fatalf("snapshot failed: %v", err) - } -} - -func TestServiceToken_GetSecretsByNameWithNotFoundSecret(t *testing.T) { - output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "secrets", "get", "TEST-SECRET-1", "TEST-SECRET-2", "FOLDER-SECRET-1", "DOES-NOT-EXIST", "--token", creds.ServiceToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--recursive", "--silent") - - if err != nil { - t.Fatalf("error running CLI command: %v", err) - } - - // Use cupaloy to snapshot test the output - err = cupaloy.Snapshot(output) - if err != nil { - t.Fatalf("snapshot failed: %v", err) - } -} - -func TestServiceToken_GetSecretsByNameWithImports(t *testing.T) { - output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "secrets", "get", "TEST-SECRET-1", "STAGING-SECRET-2", "FOLDER-SECRET-1", "--token", creds.ServiceToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--recursive", "--silent") - - if err != nil { - t.Fatalf("error running CLI command: %v", err) - } - - // Use cupaloy to snapshot test the output - err = cupaloy.Snapshot(output) - if err != nil { - t.Fatalf("snapshot failed: %v", err) - } -} - -func TestUniversalAuth_GetSecretsByNameRecursive(t *testing.T) { - MachineIdentityLoginCmd(t) - output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "secrets", "get", "TEST-SECRET-1", "TEST-SECRET-2", "FOLDER-SECRET-1", "--token", creds.UAAccessToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--recursive", "--silent") - - if err != nil { - t.Fatalf("error running CLI command: %v", err) - } - - // Use cupaloy to snapshot test the output - err = cupaloy.Snapshot(output) - if err != nil { - t.Fatalf("snapshot failed: %v", err) - } -} - -func TestUniversalAuth_GetSecretsByNameWithNotFoundSecret(t *testing.T) { - MachineIdentityLoginCmd(t) - output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "secrets", "get", "TEST-SECRET-1", "TEST-SECRET-2", "FOLDER-SECRET-1", "DOES-NOT-EXIST", "--token", creds.UAAccessToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--recursive", "--silent") - - if err != nil { - t.Fatalf("error running CLI command: %v", err) - } - - // Use cupaloy to snapshot test the output - err = cupaloy.Snapshot(output) - if err != nil { - t.Fatalf("snapshot failed: %v", err) - } -} - -func TestUniversalAuth_GetSecretsByNameWithImports(t *testing.T) { - MachineIdentityLoginCmd(t) - output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "secrets", "get", "TEST-SECRET-1", "STAGING-SECRET-2", "FOLDER-SECRET-1", "--token", creds.UAAccessToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--recursive", "--silent") - - if err != nil { - t.Fatalf("error running CLI command: %v", err) - } - - // Use cupaloy to snapshot test the output - err = cupaloy.Snapshot(output) - if err != nil { - t.Fatalf("snapshot failed: %v", err) - } -} diff --git a/cli/test/secrets_test.go b/cli/test/secrets_test.go deleted file mode 100644 index f7f0f13ff..000000000 --- a/cli/test/secrets_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package tests - -import ( - "testing" - - "github.com/bradleyjkemp/cupaloy/v2" -) - -func TestServiceToken_SecretsGetWithImportsAndRecursiveCmd(t *testing.T) { - output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "secrets", "--token", creds.ServiceToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--recursive", "--silent") - - if err != nil { - t.Fatalf("error running CLI command: %v", err) - } - - // Use cupaloy to snapshot test the output - err = cupaloy.Snapshot(output) - if err != nil { - t.Fatalf("snapshot failed: %v", err) - } -} - -func TestServiceToken_SecretsGetWithoutImportsAndWithoutRecursiveCmd(t *testing.T) { - output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "secrets", "--token", creds.ServiceToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--include-imports=false", "--silent") - - if err != nil { - t.Fatalf("error running CLI command: %v", err) - } - - // Use cupaloy to snapshot test the output - err = cupaloy.Snapshot(output) - if err != nil { - t.Fatalf("snapshot failed: %v", err) - } -} - -func TestUniversalAuth_SecretsGetWithImportsAndRecursiveCmd(t *testing.T) { - MachineIdentityLoginCmd(t) - - output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "secrets", "--token", creds.UAAccessToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--recursive", "--silent") - - if err != nil { - t.Fatalf("error running CLI command: %v", err) - } - - // Use cupaloy to snapshot test the output - err = cupaloy.Snapshot(output) - if err != nil { - t.Fatalf("snapshot failed: %v", err) - } -} - -func TestUniversalAuth_SecretsGetWithoutImportsAndWithoutRecursiveCmd(t *testing.T) { - MachineIdentityLoginCmd(t) - - output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "secrets", "--token", creds.UAAccessToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--include-imports=false", "--silent") - - if err != nil { - t.Fatalf("error running CLI command: %v", err) - } - - // Use cupaloy to snapshot test the output - err = cupaloy.Snapshot(output) - if err != nil { - t.Fatalf("snapshot failed: %v", err) - } -} - -func TestUniversalAuth_SecretsGetWrongEnvironment(t *testing.T) { - MachineIdentityLoginCmd(t) - - output, _ := ExecuteCliCommand(FORMATTED_CLI_NAME, "secrets", "--token", creds.UAAccessToken, "--projectId", creds.ProjectID, "--env", "invalid-env", "--recursive", "--silent") - - // Use cupaloy to snapshot test the output - err := cupaloy.Snapshot(output) - if err != nil { - t.Fatalf("snapshot failed: %v", err) - } - -} - -func TestUserAuth_SecretsGetAll(t *testing.T) { - output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "secrets", "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--include-imports=false", "--silent") - if err != nil { - t.Fatalf("error running CLI command: %v", err) - } - - // Use cupaloy to snapshot test the output - err = cupaloy.Snapshot(output) - if err != nil { - t.Fatalf("snapshot failed: %v", err) - } - - // explicitly called here because it should happen directly after successful secretsGetAll - // testUserAuth_SecretsGetAllWithoutConnection(t) -} - -// disabled for the time being -// func testUserAuth_SecretsGetAllWithoutConnection(t *testing.T) { -// originalConfigFile, err := util.GetConfigFile() -// if err != nil { -// t.Fatalf("error getting config file") -// } -// newConfigFile := originalConfigFile - -// // set it to a URL that will always be unreachable -// newConfigFile.LoggedInUserDomain = "http://localhost:4999" -// util.WriteConfigFile(&newConfigFile) - -// // restore config file -// defer util.WriteConfigFile(&originalConfigFile) - -// output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "secrets", "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--include-imports=false", "--silent") -// if err != nil { -// t.Fatalf("error running CLI command: %v", err) -// } - -// // Use cupaloy to snapshot test the output -// err = cupaloy.Snapshot(output) -// if err != nil { -// t.Fatalf("snapshot failed: %v", err) -// } -// } diff --git a/cli/testdata/baseline/baseline.csv b/cli/testdata/baseline/baseline.csv deleted file mode 100644 index d3f953727..000000000 --- a/cli/testdata/baseline/baseline.csv +++ /dev/null @@ -1,2 +0,0 @@ -RuleID,Commit,File,Secret,Match,StartLine,EndLine,StartColumn,EndColumn,Author,Message,Date,Email,Fingerprint -1,b,c,f,s,m,s,e,s,e,a,m,f,r,f \ No newline at end of file diff --git a/cli/testdata/baseline/baseline.json b/cli/testdata/baseline/baseline.json deleted file mode 100644 index 3a4c5427f..000000000 --- a/cli/testdata/baseline/baseline.json +++ /dev/null @@ -1,40 +0,0 @@ -[ - { - "Description": "PyPI upload token", - "StartLine": 32, - "EndLine": 32, - "StartColumn": 21, - "EndColumn": 106, - "Match": "************************", - "Secret": "************************", - "File": "detect/detect_test.go", - "Commit": "9326f35380636bcbe61e94b0584d1618c4b5c2c2", - "Entropy": 1.9606875, - "Author": "****", - "Email": "****", - "Date": "2022-03-07T14:33:06Z", - "Message": "Escape - character in regex character groups (#802)\n\n* fix char escape\n\n* add test\n\n* fix verbosity in make test", - "Tags": [], - "RuleID": "pypi-upload-token", - "Fingerprint": "9326f35380636bcbe61e94b0584d1618c4b5c2c2:detect/detect_test.go:pypi-upload-token:32" - }, - { - "Description": "PyPI upload token", - "StartLine": 33, - "EndLine": 33, - "StartColumn": 21, - "EndColumn": 106, - "Match": "************************", - "Secret": "************************", - "File": "detect/detect_test.go", - "Commit": "9326f35380636bcbe61e94b0584d1618c4b5c2c2", - "Entropy": 1.9606875, - "Author": "****", - "Email": "****", - "Date": "2022-03-07T14:33:06Z", - "Message": "Escape - character in regex character groups (#802)\n\n* fix char escape\n\n* add test\n\n* fix verbosity in make test", - "Tags": [], - "RuleID": "pypi-upload-token", - "Fingerprint": "9326f35380636bcbe61e94b0584d1618c4b5c2c2:detect/detect_test.go:pypi-upload-token:33" - } -] diff --git a/cli/testdata/baseline/baseline.sarif b/cli/testdata/baseline/baseline.sarif deleted file mode 100644 index b2f84890a..000000000 --- a/cli/testdata/baseline/baseline.sarif +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/sarif-2.1.0.json", - "version": "2.1.0", - "runs": [ - ] -} diff --git a/cli/testdata/config/allow_aws_re.toml b/cli/testdata/config/allow_aws_re.toml deleted file mode 100644 index 2e2d4f278..000000000 --- a/cli/testdata/config/allow_aws_re.toml +++ /dev/null @@ -1,9 +0,0 @@ -title = "simple config with allowlist for aws" - -[[rules]] - description = "AWS Access Key" - id = "aws-access-key" - regex = '''(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}''' - tags = ["key", "AWS"] - [rules.allowlist] - regexes = ['''AKIALALEMEL33243OLIA'''] diff --git a/cli/testdata/config/allow_commit.toml b/cli/testdata/config/allow_commit.toml deleted file mode 100644 index ee8fefd83..000000000 --- a/cli/testdata/config/allow_commit.toml +++ /dev/null @@ -1,9 +0,0 @@ -title = "simple config with allowlist for a specific commit" - -[[rules]] - description = "AWS Access Key" - id = "aws-access-key" - regex = '''(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}''' - tags = ["key", "AWS"] - [rules.allowlist] - commits = ['''allowthiscommit'''] diff --git a/cli/testdata/config/allow_global_aws_re.toml b/cli/testdata/config/allow_global_aws_re.toml deleted file mode 100644 index 7b7c3eeed..000000000 --- a/cli/testdata/config/allow_global_aws_re.toml +++ /dev/null @@ -1,8 +0,0 @@ -[[rules]] - description = "AWS Access Key" - id = "aws-access-key" - regex = '''(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}''' - tags = ["key", "AWS"] - -[allowlist] - regexes = ['''AKIALALEMEL33243OLIA'''] diff --git a/cli/testdata/config/allow_path.toml b/cli/testdata/config/allow_path.toml deleted file mode 100644 index 0fa837701..000000000 --- a/cli/testdata/config/allow_path.toml +++ /dev/null @@ -1,9 +0,0 @@ -title = "simple config with allowlist for .go files" - -[[rules]] - description = "AWS Access Key" - id = "aws-access-key" - regex = '''(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}''' - tags = ["key", "AWS"] - [rules.allowlist] - paths = ['''.go'''] diff --git a/cli/testdata/config/bad_entropy_group.toml b/cli/testdata/config/bad_entropy_group.toml deleted file mode 100755 index 8e4d1c25c..000000000 --- a/cli/testdata/config/bad_entropy_group.toml +++ /dev/null @@ -1,8 +0,0 @@ -title = "gitleaks config" - -[[rules]] -id = "discord-api-key" -description = "Discord API key" -regex = '''(?i)(discord[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-h0-9]{64})['\"]''' -secretGroup = 5 -entropy = 3.5 diff --git a/cli/testdata/config/base.toml b/cli/testdata/config/base.toml deleted file mode 100644 index ba7b2ce2c..000000000 --- a/cli/testdata/config/base.toml +++ /dev/null @@ -1,10 +0,0 @@ -title = "gitleaks config" - -[extend] -path="../testdata/config/extend_1.toml" - -[[rules]] - description = "AWS Secret Key" - id = "aws-secret-key" - regex = '''(?i)aws_(.{0,20})?=?.[\'\"0-9a-zA-Z\/+]{40}''' - tags = ["key", "AWS"] diff --git a/cli/testdata/config/entropy_group.toml b/cli/testdata/config/entropy_group.toml deleted file mode 100755 index eacfc50ea..000000000 --- a/cli/testdata/config/entropy_group.toml +++ /dev/null @@ -1,8 +0,0 @@ -title = "gitleaks config" - -[[rules]] -id = "discord-api-key" -description = "Discord API key" -regex = '''(?i)(discord[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-h0-9]{64})['\"]''' -secretGroup = 3 -entropy = 3.5 diff --git a/cli/testdata/config/escaped_character_group.toml b/cli/testdata/config/escaped_character_group.toml deleted file mode 100644 index b28039539..000000000 --- a/cli/testdata/config/escaped_character_group.toml +++ /dev/null @@ -1,8 +0,0 @@ -title = "gitleaks config" -# https://learnxinyminutes.com/docs/toml/ for toml reference - -[[rules]] - id = "pypi-upload-token" - description = "PyPI upload token" - regex = '''pypi-AgEIcHlwaS5vcmc[A-Za-z0-9\-_]{50,1000}''' - tags = ["key", "pypi"] \ No newline at end of file diff --git a/cli/testdata/config/extend_1.toml b/cli/testdata/config/extend_1.toml deleted file mode 100644 index 1f4eec0f0..000000000 --- a/cli/testdata/config/extend_1.toml +++ /dev/null @@ -1,10 +0,0 @@ -title = "gitleaks extended 1" - -[extend] -path="../testdata/config/extend_2.toml" - -[[rules]] - description = "AWS Access Key" - id = "aws-access-key" - regex = '''(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}''' - tags = ["key", "AWS"] diff --git a/cli/testdata/config/extend_2.toml b/cli/testdata/config/extend_2.toml deleted file mode 100644 index 7532c99e6..000000000 --- a/cli/testdata/config/extend_2.toml +++ /dev/null @@ -1,10 +0,0 @@ -title = "gitleaks extended 2" - -[extend] -path="../testdata/config/extend_3.toml" - -[[rules]] - description = "AWS Secret Key" - id = "aws-secret-key-again" - regex = '''(?i)aws_(.{0,20})?=?.[\'\"0-9a-zA-Z\/+]{40}''' - tags = ["key", "AWS"] diff --git a/cli/testdata/config/extend_3.toml b/cli/testdata/config/extend_3.toml deleted file mode 100644 index 47644c296..000000000 --- a/cli/testdata/config/extend_3.toml +++ /dev/null @@ -1,9 +0,0 @@ -title = "gitleaks extended 3" - -## This should not be loaded since we can only extend configs to a depth of 3 - -[[rules]] - description = "AWS Secret Key" - id = "aws-secret-key-again-again" - regex = '''(?i)aws_(.{0,20})?=?.[\'\"0-9a-zA-Z\/+]{40}''' - tags = ["key", "AWS"] diff --git a/cli/testdata/config/generic.toml b/cli/testdata/config/generic.toml deleted file mode 100644 index 625e44efc..000000000 --- a/cli/testdata/config/generic.toml +++ /dev/null @@ -1,8 +0,0 @@ -title = "gitleaks config" - -[[rules]] -description = "Generic API Key" -id = "generic-api-key" -regex = '''(?i)((key|api|token|secret|password)[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([0-9a-zA-Z\-_=]{8,64})['\"]''' -entropy = 3.7 -secretGroup = 4 diff --git a/cli/testdata/config/generic_with_py_path.toml b/cli/testdata/config/generic_with_py_path.toml deleted file mode 100644 index a528893e9..000000000 --- a/cli/testdata/config/generic_with_py_path.toml +++ /dev/null @@ -1,36 +0,0 @@ -title = "gitleaks config" - -[[rules]] -description = "Generic API Key" -id = "generic-api-key" -regex = '''(?i)((key|api|token|secret|password)[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([0-9a-zA-Z\-_=]{8,64})['\"]''' -path = '''.py''' -entropy = 3.7 -secretGroup = 4 - -[allowlist] -description = "global allow lists" -regexes = [ - '''219-09-9999''', - '''078-05-1120''', - '''(9[0-9]{2}|666)-\d{2}-\d{4}''', - '''process''', - '''getenv''', - '''\.env''', - '''env\(''', - '''env\.''', - '''setting''', - '''load''', - '''token''', - '''password''', - '''secret''', - '''api\_key''', - '''apikey''', - '''api\-key''', - ] -paths = [ - '''gitleaks.toml''', - '''(.*?)(jpg|gif|doc|pdf|bin|svg|socket)$''', - '''(go.mod|go.sum)$''' -] - diff --git a/cli/testdata/config/path_only.toml b/cli/testdata/config/path_only.toml deleted file mode 100644 index 97a8a4870..000000000 --- a/cli/testdata/config/path_only.toml +++ /dev/null @@ -1,6 +0,0 @@ -title = "gitleaks config" - -[[rules]] -description = "Python Files" -id = "python-files-only" -path = '''.py''' diff --git a/cli/testdata/config/simple.toml b/cli/testdata/config/simple.toml deleted file mode 100644 index c5fbea1c3..000000000 --- a/cli/testdata/config/simple.toml +++ /dev/null @@ -1,222 +0,0 @@ -title = "gitleaks config" -# https://learnxinyminutes.com/docs/toml/ for toml reference - -[[rules]] - description = "AWS Access Key" - id = "aws-access-key" - regex = '''(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}''' - tags = ["key", "AWS"] - -[[rules]] - description = "AWS Secret Key" - id = "aws-secret-key" - regex = '''(?i)aws_(.{0,20})?=?.[\'\"0-9a-zA-Z\/+]{40}''' - tags = ["key", "AWS"] - -[[rules]] - description = "AWS MWS key" - id = "aws-mws-key" - regex = '''amzn\.mws\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}''' - tags = ["key", "AWS", "MWS"] - -[[rules]] - description = "Facebook Secret Key" - id = "facebook-secret-key" - regex = '''(?i)(facebook|fb)(.{0,20})?(?-i)['\"][0-9a-f]{32}['\"]''' - tags = ["key", "Facebook"] - -[[rules]] - description = "Facebook Client ID" - id = "facebook-client-id" - regex = '''(?i)(facebook|fb)(.{0,20})?['\"][0-9]{13,17}['\"]''' - tags = ["key", "Facebook"] - -[[rules]] - description = "Twitter Secret Key" - id = "twitter-secret-key" - regex = '''(?i)twitter(.{0,20})?['\"][0-9a-z]{35,44}['\"]''' - tags = ["key", "Twitter"] - -[[rules]] - description = "Twitter Client ID" - id = "twitter-client-id" - regex = '''(?i)twitter(.{0,20})?['\"][0-9a-z]{18,25}['\"]''' - tags = ["client", "Twitter"] - -[[rules]] - description = "Github Personal Access Token" - id = "github-pat" - regex = '''ghp_[0-9a-zA-Z]{36}''' - tags = ["key", "Github"] -[[rules]] - description = "Github OAuth Access Token" - id = "github-oauth" - regex = '''gho_[0-9a-zA-Z]{36}''' - tags = ["key", "Github"] -[[rules]] - id = "github-app" - description = "Github App Token" - regex = '''(ghu|ghs)_[0-9a-zA-Z]{36}''' - tags = ["key", "Github"] -[[rules]] - id = "github-refresh" - description = "Github Refresh Token" - regex = '''ghr_[0-9a-zA-Z]{76}''' - tags = ["key", "Github"] - -[[rules]] - id = "linkedin-client" - description = "LinkedIn Client ID" - regex = '''(?i)linkedin(.{0,20})?(?-i)[0-9a-z]{12}''' - tags = ["client", "LinkedIn"] - -[[rules]] - id = "linkedin-secret" - description = "LinkedIn Secret Key" - regex = '''(?i)linkedin(.{0,20})?[0-9a-z]{16}''' - tags = ["secret", "LinkedIn"] - -[[rules]] - id = "slack" - description = "Slack" - regex = '''xox[baprs]-([0-9a-zA-Z]{10,48})?''' - tags = ["key", "Slack"] - -[[rules]] - id = "apkey" - description = "Asymmetric Private Key" - regex = '''-----BEGIN ((EC|PGP|DSA|RSA|OPENSSH) )?PRIVATE KEY( BLOCK)?-----''' - tags = ["key", "AsymmetricPrivateKey"] - -[[rules]] - id = "google" - description = "Google API key" - regex = '''AIza[0-9A-Za-z\-_]{35}''' - tags = ["key", "Google"] - -[[rules]] - id = "google" - description = "Google (GCP) Service Account" - regex = '''"type": "service_account"''' - tags = ["key", "Google"] - -[[rules]] - id = "heroku" - description = "Heroku API key" - regex = '''(?i)heroku(.{0,20})?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}''' - tags = ["key", "Heroku"] - -[[rules]] - id = "mailchimp" - description = "MailChimp API key" - regex = '''(?i)(mailchimp|mc)(.{0,20})?[0-9a-f]{32}-us[0-9]{1,2}''' - tags = ["key", "Mailchimp"] - -[[rules]] - id = "mailgun" - description = "Mailgun API key" - regex = '''((?i)(mailgun|mg)(.{0,20})?)?key-[0-9a-z]{32}''' - tags = ["key", "Mailgun"] - -[[rules]] - id = "paypal" - description = "PayPal Braintree access token" - regex = '''access_token\$production\$[0-9a-z]{16}\$[0-9a-f]{32}''' - tags = ["key", "Paypal"] - -[[rules]] - id = "piacatic" - description = "Picatic API key" - regex = '''sk_live_[0-9a-z]{32}''' - tags = ["key", "Picatic"] - -[[rules]] - id = "sendgrid" - description = "SendGrid API Key" - regex = '''SG\.[\w_]{16,32}\.[\w_]{16,64}''' - tags = ["key", "SendGrid"] - -[[rules]] - description = "Sidekiq Secret" - id = "sidekiq-secret" - regex = '''(?i)(?:BUNDLE_ENTERPRISE__CONTRIBSYS__COM|BUNDLE_GEMS__CONTRIBSYS__COM)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{8}:[a-f0-9]{8})(?:['|\"|\n|\r|\s|\x60|;]|$)''' - secretGroup = 1 - keywords = [ - "bundle_enterprise__contribsys__com","bundle_gems__contribsys__com", - ] - -[[rules]] - description = "Sidekiq Sensitive URL" - id = "sidekiq-sensitive-url" - regex = '''(?i)\b(http(?:s??):\/\/)([a-f0-9]{8}:[a-f0-9]{8})@(?:gems.contribsys.com|enterprise.contribsys.com)(?:[\/|\#|\?|:]|$)''' - secretGroup = 2 - keywords = [ - "gems.contribsys.com","enterprise.contribsys.com", - ] - -[[rules]] - id = "slack-webhook" - description = "Slack Webhook" - regex = '''https://hooks.slack.com/services/T[a-zA-Z0-9_]{8}/B[a-zA-Z0-9_]{8,12}/[a-zA-Z0-9_]{24}''' - tags = ["key", "slack"] - -[[rules]] - id = "stripe" - description = "Stripe API key" - regex = '''(?i)stripe(.{0,20})?[sr]k_live_[0-9a-zA-Z]{24}''' - tags = ["key", "Stripe"] - -[[rules]] - id = "square" - description = "Square access token" - regex = '''sq0atp-[0-9A-Za-z\-_]{22}''' - tags = ["key", "square"] - -[[rules]] - id = "square-oauth" - description = "Square OAuth secret" - regex = '''sq0csp-[0-9A-Za-z\-_]{43}''' - tags = ["key", "square"] - -[[rules]] - id = "twilio" - description = "Twilio API key" - regex = '''(?i)twilio(.{0,20})?SK[0-9a-f]{32}''' - tags = ["key", "twilio"] - -[[rules]] - id = "dynatrace" - description = "Dynatrace ttoken" - regex = '''dt0[a-zA-Z]{1}[0-9]{2}\.[A-Z0-9]{24}\.[A-Z0-9]{64}''' - tags = ["key", "Dynatrace"] - -[[rules]] - id = "shopify" - description = "Shopify shared secret" - regex = '''shpss_[a-fA-F0-9]{32}''' - tags = ["key", "Shopify"] - -[[rules]] - id = "shopify-access" - description = "Shopify access token" - regex = '''shpat_[a-fA-F0-9]{32}''' - tags = ["key", "Shopify"] - -[[rules]] - id = "shopify-custom" - description = "Shopify custom app access token" - regex = '''shpca_[a-fA-F0-9]{32}''' - tags = ["key", "Shopify"] - -[[rules]] - id = "shopify-private" - description = "Shopify private app access token" - regex = '''shppa_[a-fA-F0-9]{32}''' - tags = ["key", "Shopify"] - -[[rules]] - id = "pypi" - description = "PyPI upload token" - regex = '''pypi-AgEIcHlwaS5vcmc[A-Za-z0-9-_]{50,1000}''' - tags = ["key", "pypi"] - diff --git a/cli/testdata/expected/git/small-branch-foo.txt b/cli/testdata/expected/git/small-branch-foo.txt deleted file mode 100644 index b3554c7ac..000000000 --- a/cli/testdata/expected/git/small-branch-foo.txt +++ /dev/null @@ -1,17 +0,0 @@ -import ( - "fmt" - "os" -) - // seems safer - aws_token := os.Getenv("AWS_TOKEN") -package foo - -import "fmt" - -func Foo() { - fmt.Println("foo") - - // seems safe - aws_token := "AKIALALEMEL33243OLIA" - fmt.Println(aws_token) -} diff --git a/cli/testdata/expected/git/small.txt b/cli/testdata/expected/git/small.txt deleted file mode 100644 index 7235dd3a8..000000000 --- a/cli/testdata/expected/git/small.txt +++ /dev/null @@ -1,67 +0,0 @@ -import ( - "fmt" - "os" -) - // seems safer - aws_token := os.Getenv("AWS_TOKEN") -package foo - -import "fmt" - -func Foo() { - fmt.Println("foo") - - // seems safe - aws_token := "AKIALALEMEL33243OLIA" - fmt.Println(aws_token) -} -package api - -import "fmt" - -func PrintHello() { - fmt.Println("hello") -} -import ( - "fmt" - "os" -) - var a = "initial" - fmt.Println(a) - var b, c int = 1, 2 - fmt.Println(b, c) - var d = true - fmt.Println(d) - var e int - fmt.Println(e) - // load secret via env - awsToken := os.Getenv("AWS_TOKEN") - - f := "apple" - fmt.Println(f) - - // opps I added a secret at line 20 - awsToken := "AKIALALEMEL33243OLIA" -package main - -import "fmt" - -func main() { - - var a = "initial" - fmt.Println(a) - - var b, c int = 1, 2 - fmt.Println(b, c) - - var d = true - fmt.Println(d) - - var e int - fmt.Println(e) - - f := "apple" - fmt.Println(f) -} -# test -This is a repo used for testing gitleaks diff --git a/cli/testdata/expected/report/csv_simple.csv b/cli/testdata/expected/report/csv_simple.csv deleted file mode 100644 index a02ab0101..000000000 --- a/cli/testdata/expected/report/csv_simple.csv +++ /dev/null @@ -1,2 +0,0 @@ -RuleID,Commit,File,SymlinkFile,Secret,Match,StartLine,EndLine,StartColumn,EndColumn,Author,Message,Date,Email,Fingerprint -test-rule,0000000000000000,auth.py,,a secret,line containing secret,1,2,1,2,John Doe,opps,10-19-2003,johndoe@gmail.com,fingerprint diff --git a/cli/testdata/expected/report/empty.json b/cli/testdata/expected/report/empty.json deleted file mode 100644 index fe51488c7..000000000 --- a/cli/testdata/expected/report/empty.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/cli/testdata/expected/report/json_simple.json b/cli/testdata/expected/report/json_simple.json deleted file mode 100644 index c7516f118..000000000 --- a/cli/testdata/expected/report/json_simple.json +++ /dev/null @@ -1,22 +0,0 @@ -[ - { - "Description": "", - "StartLine": 1, - "EndLine": 2, - "StartColumn": 1, - "EndColumn": 2, - "Match": "line containing secret", - "Secret": "a secret", - "File": "auth.py", - "SymlinkFile": "", - "Commit": "0000000000000000", - "Entropy": 0, - "Author": "John Doe", - "Email": "johndoe@gmail.com", - "Date": "10-19-2003", - "Message": "opps", - "Tags": [], - "RuleID": "test-rule", - "Fingerprint": "" - } -] diff --git a/cli/testdata/expected/report/sarif_simple.got.sarif b/cli/testdata/expected/report/sarif_simple.got.sarif deleted file mode 100644 index 9708dd833..000000000 --- a/cli/testdata/expected/report/sarif_simple.got.sarif +++ /dev/null @@ -1,302 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/sarif-2.1.0.json", - "version": "2.1.0", - "runs": [ - { - "tool": { - "driver": { - "name": "gitleaks", - "semanticVersion": "v8.0.0", - "informationUri": "https://github.com/Infisical/infisical", - "rules": [ - { - "id": "aws-access-key", - "name": "AWS Access Key", - "shortDescription": { - "text": "(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}" - } - }, - { - "id": "aws-secret-key", - "name": "AWS Secret Key", - "shortDescription": { - "text": "(?i)aws_(.{0,20})?=?.[\\'\\\"0-9a-zA-Z\\/+]{40}" - } - }, - { - "id": "aws-mws-key", - "name": "AWS MWS key", - "shortDescription": { - "text": "amzn\\.mws\\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" - } - }, - { - "id": "facebook-secret-key", - "name": "Facebook Secret Key", - "shortDescription": { - "text": "(?i)(facebook|fb)(.{0,20})?(?-i)['\\\"][0-9a-f]{32}['\\\"]" - } - }, - { - "id": "facebook-client-id", - "name": "Facebook Client ID", - "shortDescription": { - "text": "(?i)(facebook|fb)(.{0,20})?['\\\"][0-9]{13,17}['\\\"]" - } - }, - { - "id": "twitter-secret-key", - "name": "Twitter Secret Key", - "shortDescription": { - "text": "(?i)twitter(.{0,20})?['\\\"][0-9a-z]{35,44}['\\\"]" - } - }, - { - "id": "twitter-client-id", - "name": "Twitter Client ID", - "shortDescription": { - "text": "(?i)twitter(.{0,20})?['\\\"][0-9a-z]{18,25}['\\\"]" - } - }, - { - "id": "github-pat", - "name": "Github Personal Access Token", - "shortDescription": { - "text": "ghp_[0-9a-zA-Z]{36}" - } - }, - { - "id": "github-oauth", - "name": "Github OAuth Access Token", - "shortDescription": { - "text": "gho_[0-9a-zA-Z]{36}" - } - }, - { - "id": "github-app", - "name": "Github App Token", - "shortDescription": { - "text": "(ghu|ghs)_[0-9a-zA-Z]{36}" - } - }, - { - "id": "github-refresh", - "name": "Github Refresh Token", - "shortDescription": { - "text": "ghr_[0-9a-zA-Z]{76}" - } - }, - { - "id": "linkedin-client", - "name": "LinkedIn Client ID", - "shortDescription": { - "text": "(?i)linkedin(.{0,20})?(?-i)[0-9a-z]{12}" - } - }, - { - "id": "linkedin-secret", - "name": "LinkedIn Secret Key", - "shortDescription": { - "text": "(?i)linkedin(.{0,20})?[0-9a-z]{16}" - } - }, - { - "id": "slack", - "name": "Slack", - "shortDescription": { - "text": "xox[baprs]-([0-9a-zA-Z]{10,48})?" - } - }, - { - "id": "apkey", - "name": "Asymmetric Private Key", - "shortDescription": { - "text": "-----BEGIN ((EC|PGP|DSA|RSA|OPENSSH) )?PRIVATE KEY( BLOCK)?-----" - } - }, - { - "id": "google", - "name": "Google (GCP) Service Account", - "shortDescription": { - "text": "\"type\": \"service_account\"" - } - }, - { - "id": "google", - "name": "Google (GCP) Service Account", - "shortDescription": { - "text": "\"type\": \"service_account\"" - } - }, - { - "id": "heroku", - "name": "Heroku API key", - "shortDescription": { - "text": "(?i)heroku(.{0,20})?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" - } - }, - { - "id": "mailchimp", - "name": "MailChimp API key", - "shortDescription": { - "text": "(?i)(mailchimp|mc)(.{0,20})?[0-9a-f]{32}-us[0-9]{1,2}" - } - }, - { - "id": "mailgun", - "name": "Mailgun API key", - "shortDescription": { - "text": "((?i)(mailgun|mg)(.{0,20})?)?key-[0-9a-z]{32}" - } - }, - { - "id": "paypal", - "name": "PayPal Braintree access token", - "shortDescription": { - "text": "access_token\\$production\\$[0-9a-z]{16}\\$[0-9a-f]{32}" - } - }, - { - "id": "piacatic", - "name": "Picatic API key", - "shortDescription": { - "text": "sk_live_[0-9a-z]{32}" - } - }, - { - "id": "sendgrid", - "name": "SendGrid API Key", - "shortDescription": { - "text": "SG\\.[\\w_]{16,32}\\.[\\w_]{16,64}" - } - }, - { - "id": "sidekiq-secret", - "name": "Sidekiq Secret", - "shortDescription": { - "text": "(?i)(?:BUNDLE_ENTERPRISE__CONTRIBSYS__COM|BUNDLE_GEMS__CONTRIBSYS__COM)(?:[0-9a-z\\-_\\t .]{0,20})(?:[\\s|']|[\\s|\"]){0,3}(?:=|\u003e|:=|\\|\\|:|\u003c=|=\u003e|:)(?:'|\\\"|\\s|=|\\x60){0,5}([a-f0-9]{8}:[a-f0-9]{8})(?:['|\\\"|\\n|\\r|\\s|\\x60|;]|$)" - } - }, - { - "id": "sidekiq-sensitive-url", - "name": "Sidekiq Sensitive URL", - "shortDescription": { - "text": "(?i)\\b(http(?:s??):\\/\\/)([a-f0-9]{8}:[a-f0-9]{8})@(?:gems.contribsys.com|enterprise.contribsys.com)(?:[\\/|\\#|\\?|:]|$)" - } - }, - { - "id": "slack-webhook", - "name": "Slack Webhook", - "shortDescription": { - "text": "https://hooks.slack.com/services/T[a-zA-Z0-9_]{8}/B[a-zA-Z0-9_]{8,12}/[a-zA-Z0-9_]{24}" - } - }, - { - "id": "stripe", - "name": "Stripe API key", - "shortDescription": { - "text": "(?i)stripe(.{0,20})?[sr]k_live_[0-9a-zA-Z]{24}" - } - }, - { - "id": "square", - "name": "Square access token", - "shortDescription": { - "text": "sq0atp-[0-9A-Za-z\\-_]{22}" - } - }, - { - "id": "square-oauth", - "name": "Square OAuth secret", - "shortDescription": { - "text": "sq0csp-[0-9A-Za-z\\-_]{43}" - } - }, - { - "id": "twilio", - "name": "Twilio API key", - "shortDescription": { - "text": "(?i)twilio(.{0,20})?SK[0-9a-f]{32}" - } - }, - { - "id": "dynatrace", - "name": "Dynatrace ttoken", - "shortDescription": { - "text": "dt0[a-zA-Z]{1}[0-9]{2}\\.[A-Z0-9]{24}\\.[A-Z0-9]{64}" - } - }, - { - "id": "shopify", - "name": "Shopify shared secret", - "shortDescription": { - "text": "shpss_[a-fA-F0-9]{32}" - } - }, - { - "id": "shopify-access", - "name": "Shopify access token", - "shortDescription": { - "text": "shpat_[a-fA-F0-9]{32}" - } - }, - { - "id": "shopify-custom", - "name": "Shopify custom app access token", - "shortDescription": { - "text": "shpca_[a-fA-F0-9]{32}" - } - }, - { - "id": "shopify-private", - "name": "Shopify private app access token", - "shortDescription": { - "text": "shppa_[a-fA-F0-9]{32}" - } - }, - { - "id": "pypi", - "name": "PyPI upload token", - "shortDescription": { - "text": "pypi-AgEIcHlwaS5vcmc[A-Za-z0-9-_]{50,1000}" - } - } - ] - } - }, - "results": [ - { - "message": { - "text": "test-rule has detected secret for file auth.py at commit 0000000000000000." - }, - "ruleId": "test-rule", - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "auth.py" - }, - "region": { - "startLine": 1, - "startColumn": 1, - "endLine": 2, - "endColumn": 2, - "snippet": { - "text": "a secret" - } - } - } - } - ], - "partialFingerprints": { - "commitSha": "0000000000000000", - "email": "johndoe@gmail.com", - "author": "John Doe", - "date": "10-19-2003", - "commitMessage": "opps" - } - } - ] - } - ] -} diff --git a/cli/testdata/expected/report/sarif_simple.sarif b/cli/testdata/expected/report/sarif_simple.sarif deleted file mode 100644 index 0b1b15f70..000000000 --- a/cli/testdata/expected/report/sarif_simple.sarif +++ /dev/null @@ -1,302 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/sarif-2.1.0.json", - "version": "2.1.0", - "runs": [ - { - "tool": { - "driver": { - "name": "gitleaks", - "semanticVersion": "v8.0.0", - "informationUri": "https://github.com/gitleaks/gitleaks", - "rules": [ - { - "id": "aws-access-key", - "name": "AWS Access Key", - "shortDescription": { - "text": "(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}" - } - }, - { - "id": "aws-secret-key", - "name": "AWS Secret Key", - "shortDescription": { - "text": "(?i)aws_(.{0,20})?=?.[\\'\\\"0-9a-zA-Z\\/+]{40}" - } - }, - { - "id": "aws-mws-key", - "name": "AWS MWS key", - "shortDescription": { - "text": "amzn\\.mws\\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" - } - }, - { - "id": "facebook-secret-key", - "name": "Facebook Secret Key", - "shortDescription": { - "text": "(?i)(facebook|fb)(.{0,20})?(?-i)['\\\"][0-9a-f]{32}['\\\"]" - } - }, - { - "id": "facebook-client-id", - "name": "Facebook Client ID", - "shortDescription": { - "text": "(?i)(facebook|fb)(.{0,20})?['\\\"][0-9]{13,17}['\\\"]" - } - }, - { - "id": "twitter-secret-key", - "name": "Twitter Secret Key", - "shortDescription": { - "text": "(?i)twitter(.{0,20})?['\\\"][0-9a-z]{35,44}['\\\"]" - } - }, - { - "id": "twitter-client-id", - "name": "Twitter Client ID", - "shortDescription": { - "text": "(?i)twitter(.{0,20})?['\\\"][0-9a-z]{18,25}['\\\"]" - } - }, - { - "id": "github-pat", - "name": "Github Personal Access Token", - "shortDescription": { - "text": "ghp_[0-9a-zA-Z]{36}" - } - }, - { - "id": "github-oauth", - "name": "Github OAuth Access Token", - "shortDescription": { - "text": "gho_[0-9a-zA-Z]{36}" - } - }, - { - "id": "github-app", - "name": "Github App Token", - "shortDescription": { - "text": "(ghu|ghs)_[0-9a-zA-Z]{36}" - } - }, - { - "id": "github-refresh", - "name": "Github Refresh Token", - "shortDescription": { - "text": "ghr_[0-9a-zA-Z]{76}" - } - }, - { - "id": "linkedin-client", - "name": "LinkedIn Client ID", - "shortDescription": { - "text": "(?i)linkedin(.{0,20})?(?-i)[0-9a-z]{12}" - } - }, - { - "id": "linkedin-secret", - "name": "LinkedIn Secret Key", - "shortDescription": { - "text": "(?i)linkedin(.{0,20})?[0-9a-z]{16}" - } - }, - { - "id": "slack", - "name": "Slack", - "shortDescription": { - "text": "xox[baprs]-([0-9a-zA-Z]{10,48})?" - } - }, - { - "id": "apkey", - "name": "Asymmetric Private Key", - "shortDescription": { - "text": "-----BEGIN ((EC|PGP|DSA|RSA|OPENSSH) )?PRIVATE KEY( BLOCK)?-----" - } - }, - { - "id": "google", - "name": "Google (GCP) Service Account", - "shortDescription": { - "text": "\"type\": \"service_account\"" - } - }, - { - "id": "google", - "name": "Google (GCP) Service Account", - "shortDescription": { - "text": "\"type\": \"service_account\"" - } - }, - { - "id": "heroku", - "name": "Heroku API key", - "shortDescription": { - "text": "(?i)heroku(.{0,20})?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" - } - }, - { - "id": "mailchimp", - "name": "MailChimp API key", - "shortDescription": { - "text": "(?i)(mailchimp|mc)(.{0,20})?[0-9a-f]{32}-us[0-9]{1,2}" - } - }, - { - "id": "mailgun", - "name": "Mailgun API key", - "shortDescription": { - "text": "((?i)(mailgun|mg)(.{0,20})?)?key-[0-9a-z]{32}" - } - }, - { - "id": "paypal", - "name": "PayPal Braintree access token", - "shortDescription": { - "text": "access_token\\$production\\$[0-9a-z]{16}\\$[0-9a-f]{32}" - } - }, - { - "id": "piacatic", - "name": "Picatic API key", - "shortDescription": { - "text": "sk_live_[0-9a-z]{32}" - } - }, - { - "id": "sendgrid", - "name": "SendGrid API Key", - "shortDescription": { - "text": "SG\\.[\\w_]{16,32}\\.[\\w_]{16,64}" - } - }, - { - "id": "sidekiq-secret", - "name": "Sidekiq Secret", - "shortDescription": { - "text": "(?i)(?:BUNDLE_ENTERPRISE__CONTRIBSYS__COM|BUNDLE_GEMS__CONTRIBSYS__COM)(?:[0-9a-z\\-_\\t .]{0,20})(?:[\\s|']|[\\s|\"]){0,3}(?:=|\u003e|:=|\\|\\|:|\u003c=|=\u003e|:)(?:'|\\\"|\\s|=|\\x60){0,5}([a-f0-9]{8}:[a-f0-9]{8})(?:['|\\\"|\\n|\\r|\\s|\\x60|;]|$)" - } - }, - { - "id": "sidekiq-sensitive-url", - "name": "Sidekiq Sensitive URL", - "shortDescription": { - "text": "(?i)\\b(http(?:s??):\\/\\/)([a-f0-9]{8}:[a-f0-9]{8})@(?:gems.contribsys.com|enterprise.contribsys.com)(?:[\\/|\\#|\\?|:]|$)" - } - }, - { - "id": "slack-webhook", - "name": "Slack Webhook", - "shortDescription": { - "text": "https://hooks.slack.com/services/T[a-zA-Z0-9_]{8}/B[a-zA-Z0-9_]{8,12}/[a-zA-Z0-9_]{24}" - } - }, - { - "id": "stripe", - "name": "Stripe API key", - "shortDescription": { - "text": "(?i)stripe(.{0,20})?[sr]k_live_[0-9a-zA-Z]{24}" - } - }, - { - "id": "square", - "name": "Square access token", - "shortDescription": { - "text": "sq0atp-[0-9A-Za-z\\-_]{22}" - } - }, - { - "id": "square-oauth", - "name": "Square OAuth secret", - "shortDescription": { - "text": "sq0csp-[0-9A-Za-z\\-_]{43}" - } - }, - { - "id": "twilio", - "name": "Twilio API key", - "shortDescription": { - "text": "(?i)twilio(.{0,20})?SK[0-9a-f]{32}" - } - }, - { - "id": "dynatrace", - "name": "Dynatrace ttoken", - "shortDescription": { - "text": "dt0[a-zA-Z]{1}[0-9]{2}\\.[A-Z0-9]{24}\\.[A-Z0-9]{64}" - } - }, - { - "id": "shopify", - "name": "Shopify shared secret", - "shortDescription": { - "text": "shpss_[a-fA-F0-9]{32}" - } - }, - { - "id": "shopify-access", - "name": "Shopify access token", - "shortDescription": { - "text": "shpat_[a-fA-F0-9]{32}" - } - }, - { - "id": "shopify-custom", - "name": "Shopify custom app access token", - "shortDescription": { - "text": "shpca_[a-fA-F0-9]{32}" - } - }, - { - "id": "shopify-private", - "name": "Shopify private app access token", - "shortDescription": { - "text": "shppa_[a-fA-F0-9]{32}" - } - }, - { - "id": "pypi", - "name": "PyPI upload token", - "shortDescription": { - "text": "pypi-AgEIcHlwaS5vcmc[A-Za-z0-9-_]{50,1000}" - } - } - ] - } - }, - "results": [ - { - "message": { - "text": "test-rule has detected secret for file auth.py at commit 0000000000000000." - }, - "ruleId": "test-rule", - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "auth.py" - }, - "region": { - "startLine": 1, - "startColumn": 1, - "endLine": 2, - "endColumn": 2, - "snippet": { - "text": "a secret" - } - } - } - } - ], - "partialFingerprints": { - "commitSha": "0000000000000000", - "email": "johndoe@gmail.com", - "author": "John Doe", - "date": "10-19-2003", - "commitMessage": "opps" - } - } - ] - } - ] -} diff --git a/cli/testdata/repos/nogit/main.go b/cli/testdata/repos/nogit/main.go deleted file mode 100644 index acbef43fd..000000000 --- a/cli/testdata/repos/nogit/main.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import "fmt" - -func main() { - - var a = "initial" - fmt.Println(a) - - var b, c int = 1, 2 - fmt.Println(b, c) - - var d = true - fmt.Println(d) - - var e int - fmt.Println(e) - - // opps I added a secret at line 20 - awsToken := "AKIALALEMEL33243OLIA" - - f := "apple" - fmt.Println(f) -} diff --git a/cli/testdata/repos/small/README.md b/cli/testdata/repos/small/README.md deleted file mode 100644 index 5cc9baf4d..000000000 --- a/cli/testdata/repos/small/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# test -This is a repo used for testing gitleaks diff --git a/cli/testdata/repos/small/api/api.go b/cli/testdata/repos/small/api/api.go deleted file mode 100644 index d83247911..000000000 --- a/cli/testdata/repos/small/api/api.go +++ /dev/null @@ -1,7 +0,0 @@ -package api - -import "fmt" - -func PrintHello() { - fmt.Println("hello") -} diff --git a/cli/testdata/repos/small/dotGit/COMMIT_EDITMSG b/cli/testdata/repos/small/dotGit/COMMIT_EDITMSG deleted file mode 100644 index 0ba1543fd..000000000 --- a/cli/testdata/repos/small/dotGit/COMMIT_EDITMSG +++ /dev/null @@ -1 +0,0 @@ -removing secret from foo package diff --git a/cli/testdata/repos/small/dotGit/FETCH_HEAD b/cli/testdata/repos/small/dotGit/FETCH_HEAD deleted file mode 100644 index 66c1c77ce..000000000 --- a/cli/testdata/repos/small/dotGit/FETCH_HEAD +++ /dev/null @@ -1 +0,0 @@ -2e1db472eeba53f06c4026ae4566ea022e36598e branch 'main' of github.com:gitleaks/test diff --git a/cli/testdata/repos/small/dotGit/HEAD b/cli/testdata/repos/small/dotGit/HEAD deleted file mode 100644 index b870d8262..000000000 --- a/cli/testdata/repos/small/dotGit/HEAD +++ /dev/null @@ -1 +0,0 @@ -ref: refs/heads/main diff --git a/cli/testdata/repos/small/dotGit/ORIG_HEAD b/cli/testdata/repos/small/dotGit/ORIG_HEAD deleted file mode 100644 index 96321ccd4..000000000 --- a/cli/testdata/repos/small/dotGit/ORIG_HEAD +++ /dev/null @@ -1 +0,0 @@ -1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 diff --git a/cli/testdata/repos/small/dotGit/config b/cli/testdata/repos/small/dotGit/config deleted file mode 100644 index 374df60b1..000000000 --- a/cli/testdata/repos/small/dotGit/config +++ /dev/null @@ -1,13 +0,0 @@ -[core] - repositoryformatversion = 0 - filemode = true - bare = false - logallrefupdates = true - ignorecase = true - precomposeunicode = true -[remote "origin"] - url = git@github.com:gitleaks/test.git - fetch = +refs/heads/*:refs/remotes/origin/* -[branch "main"] - remote = origin - merge = refs/heads/main diff --git a/cli/testdata/repos/small/dotGit/description b/cli/testdata/repos/small/dotGit/description deleted file mode 100644 index 498b267a8..000000000 --- a/cli/testdata/repos/small/dotGit/description +++ /dev/null @@ -1 +0,0 @@ -Unnamed repository; edit this file 'description' to name the repository. diff --git a/cli/testdata/repos/small/dotGit/index b/cli/testdata/repos/small/dotGit/index deleted file mode 100644 index fec9889ae79cb0af45c8665b198e56dd8d7c80d4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 317 zcmZ?q402{*U|<4b<|NgEnTjtrMZ;)D1_pixmjjO(7#f!_Ffe`vsu2NVgP4=MzTD)0 z%W!UW=m}>Yjr(?v>022%gIpb5d|mZ&Qy9SdU!VR2qoL+JMKj0!hLQV3kvrMX73%KP zn4g!s%d?E-_u#tY>PUcwTIjb}ZyF9^;An#aP-#~Ola9;5WE z$$I9mR{l=eFwgzzi^!9kPMmgM%fOzSn3)GLEF{R)6=qk^szME`|t0UuMCV?jzSJQU!;9}HeGn>%QuBR M$B!>J{IKCX0RPQ#yZ`_I diff --git a/cli/testdata/repos/small/dotGit/info/exclude b/cli/testdata/repos/small/dotGit/info/exclude deleted file mode 100644 index a5196d1be..000000000 --- a/cli/testdata/repos/small/dotGit/info/exclude +++ /dev/null @@ -1,6 +0,0 @@ -# git ls-files --others --exclude-from=.git/info/exclude -# Lines that start with '#' are comments. -# For a project mostly in C, the following would be a good set of -# exclude patterns (uncomment them if you want to use them): -# *.[oa] -# *~ diff --git a/cli/testdata/repos/small/dotGit/logs/HEAD b/cli/testdata/repos/small/dotGit/logs/HEAD deleted file mode 100644 index 8fc59bb3b..000000000 --- a/cli/testdata/repos/small/dotGit/logs/HEAD +++ /dev/null @@ -1,13 +0,0 @@ -0000000000000000000000000000000000000000 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 Zach Rice 1635896329 -0500 clone: from github.com:gitleaks/test.git -1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 Zach Rice 1635896362 -0500 checkout: moving from main to remove-secrets -1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 906335481df9a4b48906c90318b4fac76b67fe73 Zach Rice 1635896426 -0500 commit: load token via env var -906335481df9a4b48906c90318b4fac76b67fe73 a122b33c6bad3ee54724f52f2caad385ab1982ab Zach Rice 1635896518 -0500 commit: add api package -a122b33c6bad3ee54724f52f2caad385ab1982ab a122b33c6bad3ee54724f52f2caad385ab1982ab Zach Rice 1635896543 -0500 checkout: moving from remove-secrets to api-pkg -a122b33c6bad3ee54724f52f2caad385ab1982ab 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 Zach Rice 1635896644 -0500 checkout: moving from api-pkg to main -1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635896648 -0500 pull origin main: Fast-forward -2e1db472eeba53f06c4026ae4566ea022e36598e 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635896716 -0500 checkout: moving from main to foo -2e1db472eeba53f06c4026ae4566ea022e36598e 491504d5a31946ce75e22554cc34203d8e5ff3ca Zach Rice 1635896886 -0500 commit: adding foo package with secret -491504d5a31946ce75e22554cc34203d8e5ff3ca f1b58b97808f8e744f6a23c693859df5b5968901 Zach Rice 1635896931 -0500 commit: removing secret from foo package -f1b58b97808f8e744f6a23c693859df5b5968901 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635897009 -0500 checkout: moving from foo to main -2e1db472eeba53f06c4026ae4566ea022e36598e f1b58b97808f8e744f6a23c693859df5b5968901 Zach Rice 1635897062 -0500 checkout: moving from main to foo -f1b58b97808f8e744f6a23c693859df5b5968901 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635897508 -0500 checkout: moving from foo to main diff --git a/cli/testdata/repos/small/dotGit/logs/refs/heads/api-pkg b/cli/testdata/repos/small/dotGit/logs/refs/heads/api-pkg deleted file mode 100644 index 18e1cff1a..000000000 --- a/cli/testdata/repos/small/dotGit/logs/refs/heads/api-pkg +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 a122b33c6bad3ee54724f52f2caad385ab1982ab Zach Rice 1635896543 -0500 branch: Created from HEAD diff --git a/cli/testdata/repos/small/dotGit/logs/refs/heads/foo b/cli/testdata/repos/small/dotGit/logs/refs/heads/foo deleted file mode 100644 index 0588ad530..000000000 --- a/cli/testdata/repos/small/dotGit/logs/refs/heads/foo +++ /dev/null @@ -1,3 +0,0 @@ -0000000000000000000000000000000000000000 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635896716 -0500 branch: Created from HEAD -2e1db472eeba53f06c4026ae4566ea022e36598e 491504d5a31946ce75e22554cc34203d8e5ff3ca Zach Rice 1635896886 -0500 commit: adding foo package with secret -491504d5a31946ce75e22554cc34203d8e5ff3ca f1b58b97808f8e744f6a23c693859df5b5968901 Zach Rice 1635896931 -0500 commit: removing secret from foo package diff --git a/cli/testdata/repos/small/dotGit/logs/refs/heads/main b/cli/testdata/repos/small/dotGit/logs/refs/heads/main deleted file mode 100644 index 50148f0e8..000000000 --- a/cli/testdata/repos/small/dotGit/logs/refs/heads/main +++ /dev/null @@ -1,2 +0,0 @@ -0000000000000000000000000000000000000000 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 Zach Rice 1635896329 -0500 clone: from github.com:gitleaks/test.git -1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635896648 -0500 pull origin main: Fast-forward diff --git a/cli/testdata/repos/small/dotGit/logs/refs/heads/remove-secrets b/cli/testdata/repos/small/dotGit/logs/refs/heads/remove-secrets deleted file mode 100644 index 58344a340..000000000 --- a/cli/testdata/repos/small/dotGit/logs/refs/heads/remove-secrets +++ /dev/null @@ -1,3 +0,0 @@ -0000000000000000000000000000000000000000 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 Zach Rice 1635896362 -0500 branch: Created from HEAD -1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 906335481df9a4b48906c90318b4fac76b67fe73 Zach Rice 1635896426 -0500 commit: load token via env var -906335481df9a4b48906c90318b4fac76b67fe73 a122b33c6bad3ee54724f52f2caad385ab1982ab Zach Rice 1635896518 -0500 commit: add api package diff --git a/cli/testdata/repos/small/dotGit/logs/refs/remotes/origin/HEAD b/cli/testdata/repos/small/dotGit/logs/refs/remotes/origin/HEAD deleted file mode 100644 index a2076e59a..000000000 --- a/cli/testdata/repos/small/dotGit/logs/refs/remotes/origin/HEAD +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 Zach Rice 1635896329 -0500 clone: from github.com:gitleaks/test.git diff --git a/cli/testdata/repos/small/dotGit/logs/refs/remotes/origin/api-pkg b/cli/testdata/repos/small/dotGit/logs/refs/remotes/origin/api-pkg deleted file mode 100644 index 9c8e059cf..000000000 --- a/cli/testdata/repos/small/dotGit/logs/refs/remotes/origin/api-pkg +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 a122b33c6bad3ee54724f52f2caad385ab1982ab Zach Rice 1635896552 -0500 update by push diff --git a/cli/testdata/repos/small/dotGit/logs/refs/remotes/origin/foo b/cli/testdata/repos/small/dotGit/logs/refs/remotes/origin/foo deleted file mode 100644 index f6aed264f..000000000 --- a/cli/testdata/repos/small/dotGit/logs/refs/remotes/origin/foo +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 f1b58b97808f8e744f6a23c693859df5b5968901 Zach Rice 1635896935 -0500 update by push diff --git a/cli/testdata/repos/small/dotGit/logs/refs/remotes/origin/main b/cli/testdata/repos/small/dotGit/logs/refs/remotes/origin/main deleted file mode 100644 index 530a7894d..000000000 --- a/cli/testdata/repos/small/dotGit/logs/refs/remotes/origin/main +++ /dev/null @@ -1 +0,0 @@ -1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635896648 -0500 pull origin main: fast-forward diff --git a/cli/testdata/repos/small/dotGit/objects/02/d85657604c34e7b7fbb324a0c6c8b13c2c3760 b/cli/testdata/repos/small/dotGit/objects/02/d85657604c34e7b7fbb324a0c6c8b13c2c3760 deleted file mode 100644 index dab89999a..000000000 --- a/cli/testdata/repos/small/dotGit/objects/02/d85657604c34e7b7fbb324a0c6c8b13c2c3760 +++ /dev/null @@ -1 +0,0 @@ -xUÌ1 Â0`×ܯ8nJ––.‚ƒƒ:*(8–´\¤´ɕ$ê þw3 Noxï{Ý$6Ëf1Û~´wF'0øYbF ŠœÏTB�p�ÐãND|ƒ*]uŽCÈSÐT •…ªkLÌ>a²Ž#(ûJm–‘®Ö(©ÚsæðԴ¹]Úëé°=õ÷ô>ð°ú03 \ No newline at end of file diff --git a/cli/testdata/repos/small/dotGit/objects/15/2888a42422b2ff5868b8d003d626120a9cb738 b/cli/testdata/repos/small/dotGit/objects/15/2888a42422b2ff5868b8d003d626120a9cb738 deleted file mode 100644 index f9ada07217b81e2055ac789cce7a4e74b3e98571..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 86 zcmV-c0IC0Y0V^p=O;s>AVlXr?Ff%bx2y%6F@paY9O<{;Rx$DbK{Gumc8zsg>LAvJ2Q?U=6|e)0ejD zt)0tgetI*~(;L#%b=d>mgkO42DZqp&I%S5!poy_gGmUDhoM{pxB$LHVq=Znz(1aFo zdhLOPwG1gTHXw$ODik3}oNpMEs-{Fu31Q5LYUqFUNP-Z{GSM)~K=|mDM-W)Zc-m$*N&$nrMP2Q58jD zrn9S_a^RNz+knCIwc)g_rq{geivj;}e|3-se&PcNL%e zQ;;b4v@3VTLW-;)cgwUii_$DRy-9v ztpA;u;8UJc&<<4vIPC^{>%mtEFy7Q){)mdA%Wa9;@~OS#f?#DCm z+g3V|4{yDhfkYA1=qagX<|xr;!bzsd6A#D>1O;7fFcTTz3L-*%#1k5+r#H!0s2t=lIIJcc>)JTtswKy;@)7rXr{ dc9K%r?~rSSL-ZkcgP+n@c$zlr?hoKjPX=)8R5Snp diff --git a/cli/testdata/repos/small/dotGit/objects/5c/547e4215d9594c3935bdfefdf4f500016a4112 b/cli/testdata/repos/small/dotGit/objects/5c/547e4215d9594c3935bdfefdf4f500016a4112 deleted file mode 100644 index 5bddb82e2d848a5f668c70a9b3a5d8887c4cfc67..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51 zcmV-30L=e*0V^p=O;s>9VK6i>Ff%bxNG!9VK6i>Ff%bxNXyUHOV4Lux)Bzh;A8T9`|r&v3yz)GXrp7E J0016I4}|e<6uAHZ diff --git a/cli/testdata/repos/small/dotGit/objects/90/6335481df9a4b48906c90318b4fac76b67fe73 b/cli/testdata/repos/small/dotGit/objects/90/6335481df9a4b48906c90318b4fac76b67fe73 deleted file mode 100644 index ce4a269d3..000000000 --- a/cli/testdata/repos/small/dotGit/objects/90/6335481df9a4b48906c90318b4fac76b67fe73 +++ /dev/null @@ -1,3 +0,0 @@ -x�ŽM -Â0F]çs%™üt -"žÁ¥»I2¡E۔»ðôö ®>x¼_ªË2w@;œz㑈ءCŒXЧ@‘²Ö6 5�)–ÔÆMÖ&†ÌÎF:l'ÌFÇTHďFFÃ1–äiPüéSmðä4ÁcN×o;¦ݷV{]žߗT—˜`=�Áa€³öZ«ƒ»ü«wå ½¾d…}f�u‡�›úKKœ \ No newline at end of file diff --git a/cli/testdata/repos/small/dotGit/objects/9a/932e37eaa9fb64b09e47e5e859c9b2c8cb47ad b/cli/testdata/repos/small/dotGit/objects/9a/932e37eaa9fb64b09e47e5e859c9b2c8cb47ad deleted file mode 100644 index 5e51e39d45702fbb232d53354c28fdca96718e6a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 196 zcmV;#06YJ90bPzU3&JoEggrOQLKvy9UR0#P(i^_uI*Jq)1;)a4&r||DLB;W z;C}EY9EH@< zNm5?*h56Y^?UatmS4O8uu|%JFb(vx|wF3N!l{cJy^3FMD*&freTen98Evpv-5E*wztdudOGZmB9!Yu1$CX`zyIq5KJWzunN+wPUSfIx diff --git a/cli/testdata/repos/small/dotGit/objects/a1/22b33c6bad3ee54724f52f2caad385ab1982ab b/cli/testdata/repos/small/dotGit/objects/a1/22b33c6bad3ee54724f52f2caad385ab1982ab deleted file mode 100644 index fbcf357cc571a102e357b16d95a3538941f33de3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 163 zcmV;U09^lg0iBLP4#FT106p`H{eT7vERY!E8$5bjmPH$FX$W3?y?ukHNhX( zV6yr!x(H;hE=Y~8PATUg1qHn=Xex8Dx@cjR7*TE1WgU<-TI*djr6zLO#a(dH*2L^8 zalnkBO0bGPXKbJNk9&ZM1cv`F-NHT)?39F`+jRj@oOjcDpYQ`72gb R3eYygM*O0Os2|U*Nw*#3PL%)v diff --git a/cli/testdata/repos/small/dotGit/objects/a5/caae6d742e49a33982f1fdc608ce861ea59be5 b/cli/testdata/repos/small/dotGit/objects/a5/caae6d742e49a33982f1fdc608ce861ea59be5 deleted file mode 100644 index 8be258a32c78adf07fdfe065081c2ef755049b8d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 134 zcmV;10D1p-0ac8#3c@fH0A2IG;_;?tv1nWr95PfWjp!dpS`sO3@=BB9ApX0Z1lQy4 z+L2q_spDp{-C&;%Ju^TbCZ02r519b`5<9#w7ZTqfR<^y*eavGlXthZKh_b}wTu7L# ocOdL5Ju(k2;^~Z}n_3%fs%vGG+8@--)SCWBzd*tVz6gvg!CC@8Z~y=R diff --git a/cli/testdata/repos/small/dotGit/objects/a9/aa0c942dcef669a94f207a77426106b25efd1a b/cli/testdata/repos/small/dotGit/objects/a9/aa0c942dcef669a94f207a77426106b25efd1a deleted file mode 100644 index 9221b3c0ad1fcb47f3445b805507bb6d16a28197..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 143 zcmV;A0C4|!0V^p=O;s>7HexU|FfcPQQ3!H%bn$i7%S~a3Il1f0P5!qG=T?WFaOTmt zZ|9i4)x-b@6cP(E8Dc`}oJ4O%`dFIo{rC6FR|du`M40V^p=O;s>7Fk&z?FfcPQQ3!H%bn$i7%S~a3Il1f0P5!qG=T?WFaOTmt zZ|9i4)x-b@6cP(E8Dc`}oJ4O%`dFIo{rC6FR|du`M9WGF~X&Q45ERY)wz#Y>~ diff --git a/cli/testdata/repos/small/dotGit/objects/e5/c0849a65c586eab87dcfc31fec74f0fd7c62cb b/cli/testdata/repos/small/dotGit/objects/e5/c0849a65c586eab87dcfc31fec74f0fd7c62cb deleted file mode 100644 index 53b83ef007a757e589e3cf16407ff49717397595..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 143 zcmV;A0C4|!0V^p=O;s>7HexU|FfcPQQ3!H%bn$i7%S~a3Il1f0P5!qG=T?WFaOTmt zZ|9i4)x-b@6cP(E8Dc`}oJ4O%`dFIo{rC6FR|du`M^uWjW>vuaQEcJLJ z>L?w@o3MG_#P_-@tE`tBRQ_N~E`9EzdVKecqb%|7rS!P8eom0mOD@ltuB>-{mwfbU z*H`zpt>JN+JJIsc*E~ykv8($JK7QPL1QK*?0YJa=0E;stV3xZI#0!8$LMSlF&IaPw vI(Ib7cDQF;Xqr=crnmpd4bOc^XAerFx|G@YAb-QBa2mkt=K#Lt>myWQtA&0RcKbuip?!xQV?Zf zfK&@@521UF$9mfA_7^N3u zXRrTixpZ@3B(aO$zo#ccuc`2{8TEYKk1*gD_t0slafXGv#MH>j|!Qu&8<5X z=&9g@H9H5gM!4BzT|YqQL9(m^AIL6Ewnbinu{6W&8mTvgnJ;&2){vv)4R0Ktd{6!m zyDzcXEU)dgFL{#r_X&kvIJH!Fiq`~LZ^paf^^HDnmK174*!i3s;hP_OSAdyhrH?}L zHD>oRn+z5=+w_kYWoO>Cj0T-naJCDpM==;$p>~NcyC~Zi1;i?1%Y`P-U`FCb*x*0c zVshQm#C1NNY0^Q8hBYH~L86JHo)sN1LW?b)nOxKEDUceaddeJ*#Cfb$FD@k;fe1o# zeLvPql}d0G!#rY0P+%`g&4dh{W^IH+8Ncd`C%lS8Ji960spKsoX(#WPG9el0^89(% zthmmuy%N67Am{}wGC*8#8Lc3Pk4)c9RrzY8emU{zvB8bN_~9i8ckId8qc4J&F_V0t z4p!|~L!k2lrhD2@DL?yu+s#IG=<+Pobnt%9!xb&@Fo0^5&I$fJQkl_^!BJM{UC*J> zDUnA$`P7*(sr3-^{<5fV+roJ=$t&om7$x=uqhW)K@;xAI+lP=XWnJhQ@w5)4lEcuD zpr9E{OnZGJbe_Do8&t}KzgNQs)EwlEkpRA}lXk^w|6L>?s?q|Gu;8;wSZd-lSM{#Q_-=Kra`A&KRB1jvazbngfBjgn&`bJzAm6xn_l`)i@ zC$=@Pd*bvFwpy;t+e;!<%5R0@d7Zet#GF;fo~7=Hlc=Ph&vp!o6n)1|pNN$sT1Z;X zQc>y){CpRFa{UDYhe6&L8n?TOhz~i=EC`q!6(=61|HK}hkLcE(*k?|CwVT-A;g?UT z=s;5;+S3p#&8wavOx4Tng??U|C4|AlUdY3kO=r?4C=n+@PC%$Ew@9;2KbGKGEf37Q z%H4V%PxSS3<@(|D-+bwO7sDM0El&*ZN?i{*W@p&OYmiyY#A|QIHR%OQaDc{f^!^*T zSmn@5sDrc5Gw248+-sVLn##n)){%^_fxOWRgBen?4BXHolf^npn?%alXh}-1f|&rf zFQS2?+5P}l&wYhEFeL~sduu^rs7p-SxbmUC2zp!ajlf8ihBY|A7ynBNa(LH8G(YB? z2wUnCqj}ltPGxu%sg-*%*n%1uYQn1%*iwB67_6`BE`*N7uw%!l&et=Hx>l&pM;#&y-Ve*8=RbEwEfn>P z0V~xEboE_zOkec&aqL!eh`%a!Efe4!4;}89QhiSI0$xkVv^Mf#OFg{R?6oh=3J>&a zc>^k~(<{H+OGu})zMM^OJydKA4h_Ux#)aCu+zh&{3j*X=18Hq%v85ryB++|dpulFg zE4w;hnQT52%z8It@nLc1ROuEt+wD0V1MVSIi#{Kz=(|JOS=aKqgV*(=+X{V~8oJw$ z--c(zfxWZ_#ffPahGJ;>Rr$8hfgVaX&hN2STiMyg?wovvkw%&!6fvCVvWhsK(=gl+ zC^y`Gh&_x}G|Y4-7CK_achoE#nuNJKSB-Y$aketH_^h1r-_?Yc-pi}`e`FB{slQ|X z73zy0a0$tyMlpT>t<8 diff --git a/cli/testdata/repos/small/dotGit/packed-refs b/cli/testdata/repos/small/dotGit/packed-refs deleted file mode 100644 index 859b8c5a8..000000000 --- a/cli/testdata/repos/small/dotGit/packed-refs +++ /dev/null @@ -1,2 +0,0 @@ -# pack-refs with: peeled fully-peeled sorted -1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 refs/remotes/origin/main diff --git a/cli/testdata/repos/small/dotGit/refs/heads/api-pkg b/cli/testdata/repos/small/dotGit/refs/heads/api-pkg deleted file mode 100644 index 31b6c7893..000000000 --- a/cli/testdata/repos/small/dotGit/refs/heads/api-pkg +++ /dev/null @@ -1 +0,0 @@ -a122b33c6bad3ee54724f52f2caad385ab1982ab diff --git a/cli/testdata/repos/small/dotGit/refs/heads/foo b/cli/testdata/repos/small/dotGit/refs/heads/foo deleted file mode 100644 index 57d584841..000000000 --- a/cli/testdata/repos/small/dotGit/refs/heads/foo +++ /dev/null @@ -1 +0,0 @@ -f1b58b97808f8e744f6a23c693859df5b5968901 diff --git a/cli/testdata/repos/small/dotGit/refs/heads/main b/cli/testdata/repos/small/dotGit/refs/heads/main deleted file mode 100644 index 98f12e928..000000000 --- a/cli/testdata/repos/small/dotGit/refs/heads/main +++ /dev/null @@ -1 +0,0 @@ -2e1db472eeba53f06c4026ae4566ea022e36598e diff --git a/cli/testdata/repos/small/dotGit/refs/heads/remove-secrets b/cli/testdata/repos/small/dotGit/refs/heads/remove-secrets deleted file mode 100644 index 31b6c7893..000000000 --- a/cli/testdata/repos/small/dotGit/refs/heads/remove-secrets +++ /dev/null @@ -1 +0,0 @@ -a122b33c6bad3ee54724f52f2caad385ab1982ab diff --git a/cli/testdata/repos/small/dotGit/refs/remotes/origin/HEAD b/cli/testdata/repos/small/dotGit/refs/remotes/origin/HEAD deleted file mode 100644 index 4b0a87595..000000000 --- a/cli/testdata/repos/small/dotGit/refs/remotes/origin/HEAD +++ /dev/null @@ -1 +0,0 @@ -ref: refs/remotes/origin/main diff --git a/cli/testdata/repos/small/dotGit/refs/remotes/origin/api-pkg b/cli/testdata/repos/small/dotGit/refs/remotes/origin/api-pkg deleted file mode 100644 index 31b6c7893..000000000 --- a/cli/testdata/repos/small/dotGit/refs/remotes/origin/api-pkg +++ /dev/null @@ -1 +0,0 @@ -a122b33c6bad3ee54724f52f2caad385ab1982ab diff --git a/cli/testdata/repos/small/dotGit/refs/remotes/origin/foo b/cli/testdata/repos/small/dotGit/refs/remotes/origin/foo deleted file mode 100644 index 57d584841..000000000 --- a/cli/testdata/repos/small/dotGit/refs/remotes/origin/foo +++ /dev/null @@ -1 +0,0 @@ -f1b58b97808f8e744f6a23c693859df5b5968901 diff --git a/cli/testdata/repos/small/dotGit/refs/remotes/origin/main b/cli/testdata/repos/small/dotGit/refs/remotes/origin/main deleted file mode 100644 index 98f12e928..000000000 --- a/cli/testdata/repos/small/dotGit/refs/remotes/origin/main +++ /dev/null @@ -1 +0,0 @@ -2e1db472eeba53f06c4026ae4566ea022e36598e diff --git a/cli/testdata/repos/small/main.go b/cli/testdata/repos/small/main.go deleted file mode 100644 index 9a932e37e..000000000 --- a/cli/testdata/repos/small/main.go +++ /dev/null @@ -1,27 +0,0 @@ -package main - -import ( - "fmt" - "os" -) - -func main() { - - var a = "initial" - fmt.Println(a) - - var b, c int = 1, 2 - fmt.Println(b, c) - - var d = true - fmt.Println(d) - - var e int - fmt.Println(e) - - // load secret via env - awsToken := os.Getenv("AWS_TOKEN") - - f := "apple" - fmt.Println(f) -} diff --git a/cli/testdata/repos/staged/.gitleaksignore b/cli/testdata/repos/staged/.gitleaksignore deleted file mode 100644 index 770453ca4..000000000 --- a/cli/testdata/repos/staged/.gitleaksignore +++ /dev/null @@ -1 +0,0 @@ -api/api.go:aws-access-key:6 \ No newline at end of file diff --git a/cli/testdata/repos/staged/README.md b/cli/testdata/repos/staged/README.md deleted file mode 100644 index 5cc9baf4d..000000000 --- a/cli/testdata/repos/staged/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# test -This is a repo used for testing gitleaks diff --git a/cli/testdata/repos/staged/api/api.go b/cli/testdata/repos/staged/api/api.go deleted file mode 100644 index b16d768dd..000000000 --- a/cli/testdata/repos/staged/api/api.go +++ /dev/null @@ -1,10 +0,0 @@ -package api - -import "fmt" - -func PrintHello() { - aws_token := "AKIALALEMEL33243OLIA" // fingerprint of that secret is added to .gitleaksignore - aws_token2 := "AKIALALEMEL33243OLIA" // this one is not - fmt.Println(aws_token) - fmt.Println(aws_token2) -} diff --git a/cli/testdata/repos/staged/dotGit/COMMIT_EDITMSG b/cli/testdata/repos/staged/dotGit/COMMIT_EDITMSG deleted file mode 100644 index b83ad5393..000000000 --- a/cli/testdata/repos/staged/dotGit/COMMIT_EDITMSG +++ /dev/null @@ -1 +0,0 @@ -add .gitleaksignore file diff --git a/cli/testdata/repos/staged/dotGit/FETCH_HEAD b/cli/testdata/repos/staged/dotGit/FETCH_HEAD deleted file mode 100644 index 66c1c77ce..000000000 --- a/cli/testdata/repos/staged/dotGit/FETCH_HEAD +++ /dev/null @@ -1 +0,0 @@ -2e1db472eeba53f06c4026ae4566ea022e36598e branch 'main' of github.com:gitleaks/test diff --git a/cli/testdata/repos/staged/dotGit/HEAD b/cli/testdata/repos/staged/dotGit/HEAD deleted file mode 100644 index b870d8262..000000000 --- a/cli/testdata/repos/staged/dotGit/HEAD +++ /dev/null @@ -1 +0,0 @@ -ref: refs/heads/main diff --git a/cli/testdata/repos/staged/dotGit/ORIG_HEAD b/cli/testdata/repos/staged/dotGit/ORIG_HEAD deleted file mode 100644 index 96321ccd4..000000000 --- a/cli/testdata/repos/staged/dotGit/ORIG_HEAD +++ /dev/null @@ -1 +0,0 @@ -1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 diff --git a/cli/testdata/repos/staged/dotGit/config b/cli/testdata/repos/staged/dotGit/config deleted file mode 100644 index 374df60b1..000000000 --- a/cli/testdata/repos/staged/dotGit/config +++ /dev/null @@ -1,13 +0,0 @@ -[core] - repositoryformatversion = 0 - filemode = true - bare = false - logallrefupdates = true - ignorecase = true - precomposeunicode = true -[remote "origin"] - url = git@github.com:gitleaks/test.git - fetch = +refs/heads/*:refs/remotes/origin/* -[branch "main"] - remote = origin - merge = refs/heads/main diff --git a/cli/testdata/repos/staged/dotGit/description b/cli/testdata/repos/staged/dotGit/description deleted file mode 100644 index 498b267a8..000000000 --- a/cli/testdata/repos/staged/dotGit/description +++ /dev/null @@ -1 +0,0 @@ -Unnamed repository; edit this file 'description' to name the repository. diff --git a/cli/testdata/repos/staged/dotGit/index b/cli/testdata/repos/staged/dotGit/index deleted file mode 100644 index 42a3c4433f4d1e2ed0e9b4c42e0ca9a3c8fd5ecb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 359 zcmZ?q402{*U|<4bmK1T-AI9xbd@!1kfkB8-(e4EUL*o(#2F9;IH6lPP=O%IeNuYK% zx9sZH^DVjao!$LT>|@~9OV2FHNlnZy&P>nCFG>X}Oc7V&Q<-|u8B9-U*#I%m@EMwU z1~DgheYwg1mf_s$&=bx)8u#rS)3-8k2Dv)A_`2%lrU1>+n5M#XWgd)%nuG4H-y3tw zdaq7hI)6&lK8{L830uXvbs-E~i3OSZK&+RZ4|Eb3fZem~pE{Ucx>Olz-b++>F-p&x ztY`je 1635896329 -0500 clone: from github.com:gitleaks/test.git -1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 Zach Rice 1635896362 -0500 checkout: moving from main to remove-secrets -1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 906335481df9a4b48906c90318b4fac76b67fe73 Zach Rice 1635896426 -0500 commit: load token via env var -906335481df9a4b48906c90318b4fac76b67fe73 a122b33c6bad3ee54724f52f2caad385ab1982ab Zach Rice 1635896518 -0500 commit: add api package -a122b33c6bad3ee54724f52f2caad385ab1982ab a122b33c6bad3ee54724f52f2caad385ab1982ab Zach Rice 1635896543 -0500 checkout: moving from remove-secrets to api-pkg -a122b33c6bad3ee54724f52f2caad385ab1982ab 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 Zach Rice 1635896644 -0500 checkout: moving from api-pkg to main -1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635896648 -0500 pull origin main: Fast-forward -2e1db472eeba53f06c4026ae4566ea022e36598e 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635896716 -0500 checkout: moving from main to foo -2e1db472eeba53f06c4026ae4566ea022e36598e 491504d5a31946ce75e22554cc34203d8e5ff3ca Zach Rice 1635896886 -0500 commit: adding foo package with secret -491504d5a31946ce75e22554cc34203d8e5ff3ca f1b58b97808f8e744f6a23c693859df5b5968901 Zach Rice 1635896931 -0500 commit: removing secret from foo package -f1b58b97808f8e744f6a23c693859df5b5968901 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635897009 -0500 checkout: moving from foo to main -2e1db472eeba53f06c4026ae4566ea022e36598e f1b58b97808f8e744f6a23c693859df5b5968901 Zach Rice 1635897062 -0500 checkout: moving from main to foo -f1b58b97808f8e744f6a23c693859df5b5968901 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635897508 -0500 checkout: moving from foo to main -2e1db472eeba53f06c4026ae4566ea022e36598e bf3f24164d7256b4021575cbdb2f97b98e6f057e Rafael Figueiredo 1679239434 -0300 commit: add .gitleaksignore file diff --git a/cli/testdata/repos/staged/dotGit/logs/refs/heads/api-pkg b/cli/testdata/repos/staged/dotGit/logs/refs/heads/api-pkg deleted file mode 100644 index 18e1cff1a..000000000 --- a/cli/testdata/repos/staged/dotGit/logs/refs/heads/api-pkg +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 a122b33c6bad3ee54724f52f2caad385ab1982ab Zach Rice 1635896543 -0500 branch: Created from HEAD diff --git a/cli/testdata/repos/staged/dotGit/logs/refs/heads/foo b/cli/testdata/repos/staged/dotGit/logs/refs/heads/foo deleted file mode 100644 index 0588ad530..000000000 --- a/cli/testdata/repos/staged/dotGit/logs/refs/heads/foo +++ /dev/null @@ -1,3 +0,0 @@ -0000000000000000000000000000000000000000 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635896716 -0500 branch: Created from HEAD -2e1db472eeba53f06c4026ae4566ea022e36598e 491504d5a31946ce75e22554cc34203d8e5ff3ca Zach Rice 1635896886 -0500 commit: adding foo package with secret -491504d5a31946ce75e22554cc34203d8e5ff3ca f1b58b97808f8e744f6a23c693859df5b5968901 Zach Rice 1635896931 -0500 commit: removing secret from foo package diff --git a/cli/testdata/repos/staged/dotGit/logs/refs/heads/main b/cli/testdata/repos/staged/dotGit/logs/refs/heads/main deleted file mode 100644 index c4bd6cb68..000000000 --- a/cli/testdata/repos/staged/dotGit/logs/refs/heads/main +++ /dev/null @@ -1,3 +0,0 @@ -0000000000000000000000000000000000000000 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 Zach Rice 1635896329 -0500 clone: from github.com:gitleaks/test.git -1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635896648 -0500 pull origin main: Fast-forward -2e1db472eeba53f06c4026ae4566ea022e36598e bf3f24164d7256b4021575cbdb2f97b98e6f057e Rafael Figueiredo 1679239434 -0300 commit: add .gitleaksignore file diff --git a/cli/testdata/repos/staged/dotGit/logs/refs/heads/remove-secrets b/cli/testdata/repos/staged/dotGit/logs/refs/heads/remove-secrets deleted file mode 100644 index 58344a340..000000000 --- a/cli/testdata/repos/staged/dotGit/logs/refs/heads/remove-secrets +++ /dev/null @@ -1,3 +0,0 @@ -0000000000000000000000000000000000000000 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 Zach Rice 1635896362 -0500 branch: Created from HEAD -1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 906335481df9a4b48906c90318b4fac76b67fe73 Zach Rice 1635896426 -0500 commit: load token via env var -906335481df9a4b48906c90318b4fac76b67fe73 a122b33c6bad3ee54724f52f2caad385ab1982ab Zach Rice 1635896518 -0500 commit: add api package diff --git a/cli/testdata/repos/staged/dotGit/logs/refs/remotes/origin/HEAD b/cli/testdata/repos/staged/dotGit/logs/refs/remotes/origin/HEAD deleted file mode 100644 index a2076e59a..000000000 --- a/cli/testdata/repos/staged/dotGit/logs/refs/remotes/origin/HEAD +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 Zach Rice 1635896329 -0500 clone: from github.com:gitleaks/test.git diff --git a/cli/testdata/repos/staged/dotGit/logs/refs/remotes/origin/api-pkg b/cli/testdata/repos/staged/dotGit/logs/refs/remotes/origin/api-pkg deleted file mode 100644 index 9c8e059cf..000000000 --- a/cli/testdata/repos/staged/dotGit/logs/refs/remotes/origin/api-pkg +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 a122b33c6bad3ee54724f52f2caad385ab1982ab Zach Rice 1635896552 -0500 update by push diff --git a/cli/testdata/repos/staged/dotGit/logs/refs/remotes/origin/foo b/cli/testdata/repos/staged/dotGit/logs/refs/remotes/origin/foo deleted file mode 100644 index f6aed264f..000000000 --- a/cli/testdata/repos/staged/dotGit/logs/refs/remotes/origin/foo +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 f1b58b97808f8e744f6a23c693859df5b5968901 Zach Rice 1635896935 -0500 update by push diff --git a/cli/testdata/repos/staged/dotGit/logs/refs/remotes/origin/main b/cli/testdata/repos/staged/dotGit/logs/refs/remotes/origin/main deleted file mode 100644 index 530a7894d..000000000 --- a/cli/testdata/repos/staged/dotGit/logs/refs/remotes/origin/main +++ /dev/null @@ -1 +0,0 @@ -1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 2e1db472eeba53f06c4026ae4566ea022e36598e Zach Rice 1635896648 -0500 pull origin main: fast-forward diff --git a/cli/testdata/repos/staged/dotGit/objects/02/d85657604c34e7b7fbb324a0c6c8b13c2c3760 b/cli/testdata/repos/staged/dotGit/objects/02/d85657604c34e7b7fbb324a0c6c8b13c2c3760 deleted file mode 100644 index dab89999a..000000000 --- a/cli/testdata/repos/staged/dotGit/objects/02/d85657604c34e7b7fbb324a0c6c8b13c2c3760 +++ /dev/null @@ -1 +0,0 @@ -xUÌ1 Â0`×ܯ8nJ––.‚ƒƒ:*(8–´\¤´ɕ$ê þw3 Noxï{Ý$6Ëf1Û~´wF'0øYbF ŠœÏTB�p�ÐãND|ƒ*]uŽCÈSÐT •…ªkLÌ>a²Ž#(ûJm–‘®Ö(©ÚsæðԴ¹]Úëé°=õ÷ô>ð°ú03 \ No newline at end of file diff --git a/cli/testdata/repos/staged/dotGit/objects/15/2888a42422b2ff5868b8d003d626120a9cb738 b/cli/testdata/repos/staged/dotGit/objects/15/2888a42422b2ff5868b8d003d626120a9cb738 deleted file mode 100644 index f9ada07217b81e2055ac789cce7a4e74b3e98571..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 86 zcmV-c0IC0Y0V^p=O;s>AVlXr?Ff%bx2y%6F@paY9O<{;Rx$DbK{Gumc8zsg>LAvJ2Q?U=6|e)0ejD zt)0tgetI*~(;L#%b=d>mgkO42DZqp&I%S5!poy_gGmUDhoM{pxB$LHVq=Znz(1aFo zdhLOPwG1gTHXw$ODik3}oNpMEs-{Fu31Q5LYUqFUNP-Z{GSM)~K=|mDM-W)Zc-m$*N&$nrMP2Q58jD zrn9S_a^RNz+knCIwc)g_rq{geivj;}e|3-se&PcNL%e zQ;;b4v@3VTLW-;)cgwUii_$DRy-9v ztpA;u;8UJc&<<4vIPC^{>%mtEFy7Q){)mdA%Wa9;@~OS#f?#DCm8U`Q;;)CXd{^n9zt@?zb@ z+g3V|4{yDhfkYA1=qagX<|xr;!bzsd6A#D>1O;7fFcTTz3L-*%#1k5+r#H!0s2t=lIIJcc>)JTtswKy;@)7rXr{ dc9K%r?~rSSL-ZkcgP+n@c$zlr?hoKjPX=)8R5Snp diff --git a/cli/testdata/repos/staged/dotGit/objects/5c/547e4215d9594c3935bdfefdf4f500016a4112 b/cli/testdata/repos/staged/dotGit/objects/5c/547e4215d9594c3935bdfefdf4f500016a4112 deleted file mode 100644 index 5bddb82e2d848a5f668c70a9b3a5d8887c4cfc67..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51 zcmV-30L=e*0V^p=O;s>9VK6i>Ff%bxNG!40ZYosPf{>6G+-!5OwLYBPgO`P$mHV6%q_?-Dp62M%Pmpj;z}#cOI8Rd z%FHYANX^N~*U(g`=Hg5&FODzC&rZ!#u(DN9a`g6e^l|iY^>y_zHa0Rb_V@90RN~?U W>emBn%*oS$YtZDX7F=a3`FfcPQQP4}zEXhet%r4GM&&w}LWpI-m=4`OfZsC-y;=2Dv)A_`2%lrZB{u-1X%q|67K0t3yvX^Jv_+b4=fAVgLjRi3OPq zF(GwMqBkRbEKT?R`}^f917nt>5Y&j=#LPUs^n8X{ll9DBt^A#`VV?Wb7m+77ojC2j J767+rJXdN*O&|aO diff --git a/cli/testdata/repos/staged/dotGit/objects/78/9ba677976d5db481de55c799d67acbf8e3f16a b/cli/testdata/repos/staged/dotGit/objects/78/9ba677976d5db481de55c799d67acbf8e3f16a deleted file mode 100644 index 93acde0f58e563f655dbebe3a98dbf7b2cd06937..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51 zcmV-30L=e*0V^p=O;s>9VK6i>Ff%bxNXyUHOV4Lux)Bzh;A8T9`|r&v3yz)GXrp7E J0016I4}|e<6uAHZ diff --git a/cli/testdata/repos/staged/dotGit/objects/90/6335481df9a4b48906c90318b4fac76b67fe73 b/cli/testdata/repos/staged/dotGit/objects/90/6335481df9a4b48906c90318b4fac76b67fe73 deleted file mode 100644 index ce4a269d3..000000000 --- a/cli/testdata/repos/staged/dotGit/objects/90/6335481df9a4b48906c90318b4fac76b67fe73 +++ /dev/null @@ -1,3 +0,0 @@ -x�ŽM -Â0F]çs%™üt -"žÁ¥»I2¡E۔»ðôö ®>x¼_ªË2w@;œz㑈ءCŒXЧ@‘²Ö6 5�)–ÔÆMÖ&†ÌÎF:l'ÌFÇTHďFFÃ1–äiPüéSmðä4ÁcN×o;¦ݷV{]žߗT—˜`=�Áa€³öZ«ƒ»ü«wå ½¾d…}f�u‡�›úKKœ \ No newline at end of file diff --git a/cli/testdata/repos/staged/dotGit/objects/9a/932e37eaa9fb64b09e47e5e859c9b2c8cb47ad b/cli/testdata/repos/staged/dotGit/objects/9a/932e37eaa9fb64b09e47e5e859c9b2c8cb47ad deleted file mode 100644 index 5e51e39d45702fbb232d53354c28fdca96718e6a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 196 zcmV;#06YJ90bPzU3&JoEggrOQLKvy9UR0#P(i^_uI*Jq)1;)a4&r||DLB;W z;C}EY9EH@< zNm5?*h56Y^?UatmS4O8uu|%JFb(vx|wF3N!l{cJy^3FMD*&freTen98Evpv-5E*wztdudOGZmB9!Yu1$CX`zyIq5KJWzunN+wPUSfIx diff --git a/cli/testdata/repos/staged/dotGit/objects/a1/22b33c6bad3ee54724f52f2caad385ab1982ab b/cli/testdata/repos/staged/dotGit/objects/a1/22b33c6bad3ee54724f52f2caad385ab1982ab deleted file mode 100644 index fbcf357cc571a102e357b16d95a3538941f33de3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 163 zcmV;U09^lg0iBLP4#FT106p`H{eT7vERY!E8$5bjmPH$FX$W3?y?ukHNhX( zV6yr!x(H;hE=Y~8PATUg1qHn=Xex8Dx@cjR7*TE1WgU<-TI*djr6zLO#a(dH*2L^8 zalnkBO0bGPXKbJNk9&ZM1cv`F-NHT)?39F`+jRj@oOjcDpYQ`72gb R3eYygM*O0Os2|U*Nw*#3PL%)v diff --git a/cli/testdata/repos/staged/dotGit/objects/a5/caae6d742e49a33982f1fdc608ce861ea59be5 b/cli/testdata/repos/staged/dotGit/objects/a5/caae6d742e49a33982f1fdc608ce861ea59be5 deleted file mode 100644 index 8be258a32c78adf07fdfe065081c2ef755049b8d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 134 zcmV;10D1p-0ac8#3c@fH0A2IG;_;?tv1nWr95PfWjp!dpS`sO3@=BB9ApX0Z1lQy4 z+L2q_spDp{-C&;%Ju^TbCZ02r519b`5<9#w7ZTqfR<^y*eavGlXthZKh_b}wTu7L# ocOdL5Ju(k2;^~Z}n_3%fs%vGG+8@--)SCWBzd*tVz6gvg!CC@8Z~y=R diff --git a/cli/testdata/repos/staged/dotGit/objects/a9/aa0c942dcef669a94f207a77426106b25efd1a b/cli/testdata/repos/staged/dotGit/objects/a9/aa0c942dcef669a94f207a77426106b25efd1a deleted file mode 100644 index 9221b3c0ad1fcb47f3445b805507bb6d16a28197..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 143 zcmV;A0C4|!0V^p=O;s>7HexU|FfcPQQ3!H%bn$i7%S~a3Il1f0P5!qG=T?WFaOTmt zZ|9i4)x-b@6cP(E8Dc`}oJ4O%`dFIo{rC6FR|du`MDU~-W38KgBEui|BvcRe1i2bg{QR>~g8%>k diff --git a/cli/testdata/repos/staged/dotGit/objects/bc/f47ef84f29bb7ed6e653d61fccd30d0ecce886 b/cli/testdata/repos/staged/dotGit/objects/bc/f47ef84f29bb7ed6e653d61fccd30d0ecce886 deleted file mode 100644 index ec618b7a930fa1ad86255b0c385d1d9e439ff27d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 116 zcmV-)0E_>40V^p=O;s>7Fk&z?FfcPQQ3!H%bn$i7%S~a3Il1f0P5!qG=T?WFaOTmt zZ|9i4)x-b@6cP(E8Dc`}oJ4O%`dFIo{rC6FR|du`M®­ӷÅF_uÝQ;r£S/ÿã²jozH[Ûó™&Y‚uÁ;OŸì˜Mú;;ðfÆhÎtXëؠ?ϺÞ[•ºÁü_]U \ No newline at end of file diff --git a/cli/testdata/repos/staged/dotGit/objects/d8/32479114dc6be7207edc7c37ce91dd11b93161 b/cli/testdata/repos/staged/dotGit/objects/d8/32479114dc6be7207edc7c37ce91dd11b93161 deleted file mode 100644 index 0bd9a371a3ad63ecb59017199a81859c334ad129..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 80 zcmV-W0I&ae0ZYosPf{>9WGF~X&Q45ERY)wz#Y>~ diff --git a/cli/testdata/repos/staged/dotGit/objects/e5/c0849a65c586eab87dcfc31fec74f0fd7c62cb b/cli/testdata/repos/staged/dotGit/objects/e5/c0849a65c586eab87dcfc31fec74f0fd7c62cb deleted file mode 100644 index 53b83ef007a757e589e3cf16407ff49717397595..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 143 zcmV;A0C4|!0V^p=O;s>7HexU|FfcPQQ3!H%bn$i7%S~a3Il1f0P5!qG=T?WFaOTmt zZ|9i4)x-b@6cP(E8Dc`}oJ4O%`dFIo{rC6FR|du`M^uWjW>vuaQEcJLJ z>L?w@o3MG_#P_-@tE`tBRQ_N~E`9EzdVKecqb%|7rS!P8eom0mOD@ltuB>-{mwfbU z*H`zpt>JN+JJIsc*E~ykv8($JK7QPL1QK*?0YJa=0E;stV3xZI#0!8$LMSlF&IaPw vI(Ib7cDQF;Xqr=crnmpd4bOc^XAerFx|G@YAb-QBa2mkt=K#Lt>myWQtA&0RcKbuip?!xQV?Zf zfK&@@521UF$9mfA_7^N3u zXRrTixpZ@3B(aO$zo#ccuc`2{8TEYKk1*gD_t0slafXGv#MH>j|!Qu&8<5X z=&9g@H9H5gM!4BzT|YqQL9(m^AIL6Ewnbinu{6W&8mTvgnJ;&2){vv)4R0Ktd{6!m zyDzcXEU)dgFL{#r_X&kvIJH!Fiq`~LZ^paf^^HDnmK174*!i3s;hP_OSAdyhrH?}L zHD>oRn+z5=+w_kYWoO>Cj0T-naJCDpM==;$p>~NcyC~Zi1;i?1%Y`P-U`FCb*x*0c zVshQm#C1NNY0^Q8hBYH~L86JHo)sN1LW?b)nOxKEDUceaddeJ*#Cfb$FD@k;fe1o# zeLvPql}d0G!#rY0P+%`g&4dh{W^IH+8Ncd`C%lS8Ji960spKsoX(#WPG9el0^89(% zthmmuy%N67Am{}wGC*8#8Lc3Pk4)c9RrzY8emU{zvB8bN_~9i8ckId8qc4J&F_V0t z4p!|~L!k2lrhD2@DL?yu+s#IG=<+Pobnt%9!xb&@Fo0^5&I$fJQkl_^!BJM{UC*J> zDUnA$`P7*(sr3-^{<5fV+roJ=$t&om7$x=uqhW)K@;xAI+lP=XWnJhQ@w5)4lEcuD zpr9E{OnZGJbe_Do8&t}KzgNQs)EwlEkpRA}lXk^w|6L>?s?q|Gu;8;wSZd-lSM{#Q_-=Kra`A&KRB1jvazbngfBjgn&`bJzAm6xn_l`)i@ zC$=@Pd*bvFwpy;t+e;!<%5R0@d7Zet#GF;fo~7=Hlc=Ph&vp!o6n)1|pNN$sT1Z;X zQc>y){CpRFa{UDYhe6&L8n?TOhz~i=EC`q!6(=61|HK}hkLcE(*k?|CwVT-A;g?UT z=s;5;+S3p#&8wavOx4Tng??U|C4|AlUdY3kO=r?4C=n+@PC%$Ew@9;2KbGKGEf37Q z%H4V%PxSS3<@(|D-+bwO7sDM0El&*ZN?i{*W@p&OYmiyY#A|QIHR%OQaDc{f^!^*T zSmn@5sDrc5Gw248+-sVLn##n)){%^_fxOWRgBen?4BXHolf^npn?%alXh}-1f|&rf zFQS2?+5P}l&wYhEFeL~sduu^rs7p-SxbmUC2zp!ajlf8ihBY|A7ynBNa(LH8G(YB? z2wUnCqj}ltPGxu%sg-*%*n%1uYQn1%*iwB67_6`BE`*N7uw%!l&et=Hx>l&pM;#&y-Ve*8=RbEwEfn>P z0V~xEboE_zOkec&aqL!eh`%a!Efe4!4;}89QhiSI0$xkVv^Mf#OFg{R?6oh=3J>&a zc>^k~(<{H+OGu})zMM^OJydKA4h_Ux#)aCu+zh&{3j*X=18Hq%v85ryB++|dpulFg zE4w;hnQT52%z8It@nLc1ROuEt+wD0V1MVSIi#{Kz=(|JOS=aKqgV*(=+X{V~8oJw$ z--c(zfxWZ_#ffPahGJ;>Rr$8hfgVaX&hN2STiMyg?wovvkw%&!6fvCVvWhsK(=gl+ zC^y`Gh&_x}G|Y4-7CK_achoE#nuNJKSB-Y$aketH_^h1r-_?Yc-pi}`e`FB{slQ|X z73zy0a0$tyMlpT>t<8 diff --git a/cli/testdata/repos/staged/dotGit/packed-refs b/cli/testdata/repos/staged/dotGit/packed-refs deleted file mode 100644 index 859b8c5a8..000000000 --- a/cli/testdata/repos/staged/dotGit/packed-refs +++ /dev/null @@ -1,2 +0,0 @@ -# pack-refs with: peeled fully-peeled sorted -1b6da43b82b22e4eaa10bcf8ee591e91abbfc587 refs/remotes/origin/main diff --git a/cli/testdata/repos/staged/dotGit/refs/heads/api-pkg b/cli/testdata/repos/staged/dotGit/refs/heads/api-pkg deleted file mode 100644 index 31b6c7893..000000000 --- a/cli/testdata/repos/staged/dotGit/refs/heads/api-pkg +++ /dev/null @@ -1 +0,0 @@ -a122b33c6bad3ee54724f52f2caad385ab1982ab diff --git a/cli/testdata/repos/staged/dotGit/refs/heads/foo b/cli/testdata/repos/staged/dotGit/refs/heads/foo deleted file mode 100644 index 57d584841..000000000 --- a/cli/testdata/repos/staged/dotGit/refs/heads/foo +++ /dev/null @@ -1 +0,0 @@ -f1b58b97808f8e744f6a23c693859df5b5968901 diff --git a/cli/testdata/repos/staged/dotGit/refs/heads/main b/cli/testdata/repos/staged/dotGit/refs/heads/main deleted file mode 100644 index a06d4d30f..000000000 --- a/cli/testdata/repos/staged/dotGit/refs/heads/main +++ /dev/null @@ -1 +0,0 @@ -bf3f24164d7256b4021575cbdb2f97b98e6f057e diff --git a/cli/testdata/repos/staged/dotGit/refs/heads/remove-secrets b/cli/testdata/repos/staged/dotGit/refs/heads/remove-secrets deleted file mode 100644 index 31b6c7893..000000000 --- a/cli/testdata/repos/staged/dotGit/refs/heads/remove-secrets +++ /dev/null @@ -1 +0,0 @@ -a122b33c6bad3ee54724f52f2caad385ab1982ab diff --git a/cli/testdata/repos/staged/dotGit/refs/remotes/origin/HEAD b/cli/testdata/repos/staged/dotGit/refs/remotes/origin/HEAD deleted file mode 100644 index 4b0a87595..000000000 --- a/cli/testdata/repos/staged/dotGit/refs/remotes/origin/HEAD +++ /dev/null @@ -1 +0,0 @@ -ref: refs/remotes/origin/main diff --git a/cli/testdata/repos/staged/dotGit/refs/remotes/origin/api-pkg b/cli/testdata/repos/staged/dotGit/refs/remotes/origin/api-pkg deleted file mode 100644 index 31b6c7893..000000000 --- a/cli/testdata/repos/staged/dotGit/refs/remotes/origin/api-pkg +++ /dev/null @@ -1 +0,0 @@ -a122b33c6bad3ee54724f52f2caad385ab1982ab diff --git a/cli/testdata/repos/staged/dotGit/refs/remotes/origin/foo b/cli/testdata/repos/staged/dotGit/refs/remotes/origin/foo deleted file mode 100644 index 57d584841..000000000 --- a/cli/testdata/repos/staged/dotGit/refs/remotes/origin/foo +++ /dev/null @@ -1 +0,0 @@ -f1b58b97808f8e744f6a23c693859df5b5968901 diff --git a/cli/testdata/repos/staged/dotGit/refs/remotes/origin/main b/cli/testdata/repos/staged/dotGit/refs/remotes/origin/main deleted file mode 100644 index 98f12e928..000000000 --- a/cli/testdata/repos/staged/dotGit/refs/remotes/origin/main +++ /dev/null @@ -1 +0,0 @@ -2e1db472eeba53f06c4026ae4566ea022e36598e diff --git a/cli/testdata/repos/staged/main.go b/cli/testdata/repos/staged/main.go deleted file mode 100644 index 9a932e37e..000000000 --- a/cli/testdata/repos/staged/main.go +++ /dev/null @@ -1,27 +0,0 @@ -package main - -import ( - "fmt" - "os" -) - -func main() { - - var a = "initial" - fmt.Println(a) - - var b, c int = 1, 2 - fmt.Println(b, c) - - var d = true - fmt.Println(d) - - var e int - fmt.Println(e) - - // load secret via env - awsToken := os.Getenv("AWS_TOKEN") - - f := "apple" - fmt.Println(f) -} diff --git a/cli/testdata/repos/symlinks/file_symlink/symlinked_id_ed25519 b/cli/testdata/repos/symlinks/file_symlink/symlinked_id_ed25519 deleted file mode 120000 index fd0203d88..000000000 --- a/cli/testdata/repos/symlinks/file_symlink/symlinked_id_ed25519 +++ /dev/null @@ -1 +0,0 @@ -../source_file/id_ed25519 \ No newline at end of file diff --git a/cli/testdata/repos/symlinks/source_file/id_ed25519 b/cli/testdata/repos/symlinks/source_file/id_ed25519 deleted file mode 100644 index 9f4ff614b..000000000 --- a/cli/testdata/repos/symlinks/source_file/id_ed25519 +++ /dev/null @@ -1,7 +0,0 @@ ------BEGIN OPENSSH PRIVATE KEY----- -b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW -QyNTUxOQAAACA8YWKYztuuvxUIMomc3zv0OdXCT57Cc2cRYu3TMbX9XAAAAJDiKO3C4ijt -wgAAAAtzc2gtZWQyNTUxOQAAACA8YWKYztuuvxUIMomc3zv0OdXCT57Cc2cRYu3TMbX9XA -AAAECzmj8DGxg5YHtBK4AmBttMXDQHsPAaCyYHQjJ4YujRBTxhYpjO266/FQgyiZzfO/Q5 -1cJPnsJzZxFi7dMxtf1cAAAADHJvb3RAZGV2aG9zdAE= ------END OPENSSH PRIVATE KEY----- diff --git a/cli/testdata/tmp/note.txt b/cli/testdata/tmp/note.txt deleted file mode 100644 index 429d60380..000000000 --- a/cli/testdata/tmp/note.txt +++ /dev/null @@ -1 +0,0 @@ -nothing should be saved here diff --git a/cli/upload_to_cloudsmith.sh b/cli/upload_to_cloudsmith.sh deleted file mode 100755 index 32a3694a4..000000000 --- a/cli/upload_to_cloudsmith.sh +++ /dev/null @@ -1,21 +0,0 @@ -cd dist -for i in *.apk; do - [ -f "$i" ] || break - cloudsmith push alpine --republish infisical/infisical-cli/alpine/any-version $i -done - -# for i in *.deb; do -# [ -f "$i" ] || break -# cloudsmith push deb --republish infisical/infisical-cli/any-distro/any-version $i -# done - -for i in *.deb; do - [ -f "$i" ] || break - deb-s3 upload --bucket=$INFISICAL_CLI_S3_BUCKET --prefix=deb --visibility=private --sign=$INFISICAL_CLI_REPO_SIGNING_KEY_ID --preserve-versions $i -done - - -for i in *.rpm; do - [ -f "$i" ] || break - cloudsmith push rpm --republish infisical/infisical-cli/any-distro/any-version $i -done From 9f0250caf265226f820109aed4a005f06090d5b4 Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Tue, 29 Jul 2025 20:54:55 +0800 Subject: [PATCH 2/2] misc: removed unnecessary CLI files in root --- .../workflows/release_build_infisical_cli.yml | 153 ----------- .github/workflows/run-cli-tests.yml | 55 ---- .goreleaser.yaml | 241 ------------------ npm/.eslintrc.json | 9 - npm/README.md | 68 ----- npm/package-lock.json | 141 ---------- npm/package.json | 25 -- npm/src/index.cjs | 166 ------------ 8 files changed, 858 deletions(-) delete mode 100644 .github/workflows/release_build_infisical_cli.yml delete mode 100644 .github/workflows/run-cli-tests.yml delete mode 100644 .goreleaser.yaml delete mode 100644 npm/.eslintrc.json delete mode 100644 npm/README.md delete mode 100644 npm/package-lock.json delete mode 100644 npm/package.json delete mode 100644 npm/src/index.cjs diff --git a/.github/workflows/release_build_infisical_cli.yml b/.github/workflows/release_build_infisical_cli.yml deleted file mode 100644 index 1c16b00b3..000000000 --- a/.github/workflows/release_build_infisical_cli.yml +++ /dev/null @@ -1,153 +0,0 @@ -name: Build and release CLI - -on: - workflow_dispatch: - - push: - # run only against tags - tags: - - "infisical-cli/v*.*.*" - -permissions: - contents: write - -jobs: - cli-integration-tests: - name: Run tests before deployment - uses: ./.github/workflows/run-cli-tests.yml - secrets: - CLI_TESTS_UA_CLIENT_ID: ${{ secrets.CLI_TESTS_UA_CLIENT_ID }} - CLI_TESTS_UA_CLIENT_SECRET: ${{ secrets.CLI_TESTS_UA_CLIENT_SECRET }} - CLI_TESTS_SERVICE_TOKEN: ${{ secrets.CLI_TESTS_SERVICE_TOKEN }} - CLI_TESTS_PROJECT_ID: ${{ secrets.CLI_TESTS_PROJECT_ID }} - CLI_TESTS_ENV_SLUG: ${{ secrets.CLI_TESTS_ENV_SLUG }} - CLI_TESTS_USER_EMAIL: ${{ secrets.CLI_TESTS_USER_EMAIL }} - CLI_TESTS_USER_PASSWORD: ${{ secrets.CLI_TESTS_USER_PASSWORD }} - CLI_TESTS_INFISICAL_VAULT_FILE_PASSPHRASE: ${{ secrets.CLI_TESTS_INFISICAL_VAULT_FILE_PASSPHRASE }} - - npm-release: - runs-on: ubuntu-latest - env: - working-directory: ./npm - needs: - - cli-integration-tests - - goreleaser - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Extract version - run: | - VERSION=$(echo ${{ github.ref_name }} | sed 's/infisical-cli\/v//') - echo "Version extracted: $VERSION" - echo "CLI_VERSION=$VERSION" >> $GITHUB_ENV - - - name: Print version - run: echo ${{ env.CLI_VERSION }} - - - name: Setup Node - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0 - with: - node-version: 20 - cache: "npm" - cache-dependency-path: ./npm/package-lock.json - - name: Install dependencies - working-directory: ${{ env.working-directory }} - run: npm install --ignore-scripts - - - name: Set NPM version - working-directory: ${{ env.working-directory }} - run: npm version ${{ env.CLI_VERSION }} --allow-same-version --no-git-tag-version - - - name: Setup NPM - working-directory: ${{ env.working-directory }} - run: | - echo 'registry="https://registry.npmjs.org/"' > ./.npmrc - echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ./.npmrc - - echo 'registry="https://registry.npmjs.org/"' > ~/.npmrc - echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - - - name: Pack NPM - working-directory: ${{ env.working-directory }} - run: npm pack - - - name: Publish NPM - working-directory: ${{ env.working-directory }} - run: npm publish --tarball=./infisical-sdk-${{github.ref_name}} --access public --registry=https://registry.npmjs.org/ - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - goreleaser: - runs-on: ubuntu-latest-8-cores - needs: [cli-integration-tests] - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: 🐋 Login to Docker Hub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: 🔧 Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - run: git fetch --force --tags - - run: echo "Ref name ${{github.ref_name}}" - - uses: actions/setup-go@v3 - with: - go-version: ">=1.19.3" - cache: true - cache-dependency-path: cli/go.sum - - name: Setup for libssl1.0-dev - run: | - echo 'deb http://security.ubuntu.com/ubuntu bionic-security main' | sudo tee -a /etc/apt/sources.list - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32 - sudo apt update - sudo apt-get install -y libssl1.0-dev - - name: OSXCross for CGO Support - run: | - mkdir ../../osxcross - git clone https://github.com/plentico/osxcross-target.git ../../osxcross/target - - uses: goreleaser/goreleaser-action@v4 - with: - distribution: goreleaser-pro - version: v1.26.2-pro - 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 }} - - uses: actions/setup-python@v4 - - run: pip install --upgrade cloudsmith-cli - - uses: ruby/setup-ruby@354a1ad156761f5ee2b7b13fa8e09943a5e8d252 - with: - ruby-version: "3.3" # Not needed with a .ruby-version, .tool-versions or mise.toml - bundler-cache: true # runs 'bundle install' and caches installed gems automatically - - name: Install deb-s3 - run: gem install deb-s3 - - name: Configure GPG Key - run: echo -n "$GPG_SIGNING_KEY" | base64 --decode | gpg --batch --import - env: - GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }} - GPG_SIGNING_KEY_PASSPHRASE: ${{ secrets.GPG_SIGNING_KEY_PASSPHRASE }} - - name: Publish to CloudSmith - run: sh cli/upload_to_cloudsmith.sh - env: - CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }} - INFISICAL_CLI_S3_BUCKET: ${{ secrets.INFISICAL_CLI_S3_BUCKET }} - INFISICAL_CLI_REPO_SIGNING_KEY_ID: ${{ secrets.INFISICAL_CLI_REPO_SIGNING_KEY_ID }} - AWS_ACCESS_KEY_ID: ${{ secrets.INFISICAL_CLI_REPO_AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.INFISICAL_CLI_REPO_AWS_SECRET_ACCESS_KEY }} - - name: Invalidate Cloudfront cache - run: aws cloudfront create-invalidation --distribution-id $CLOUDFRONT_DISTRIBUTION_ID --paths '/deb/dists/stable/*' - env: - AWS_ACCESS_KEY_ID: ${{ secrets.INFISICAL_CLI_REPO_AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.INFISICAL_CLI_REPO_AWS_SECRET_ACCESS_KEY }} - CLOUDFRONT_DISTRIBUTION_ID: ${{ secrets.INFISICAL_CLI_REPO_CLOUDFRONT_DISTRIBUTION_ID }} diff --git a/.github/workflows/run-cli-tests.yml b/.github/workflows/run-cli-tests.yml deleted file mode 100644 index da6f507a7..000000000 --- a/.github/workflows/run-cli-tests.yml +++ /dev/null @@ -1,55 +0,0 @@ -name: Go CLI Tests - -on: - pull_request: - types: [opened, synchronize] - paths: - - "cli/**" - - workflow_dispatch: - - workflow_call: - secrets: - CLI_TESTS_UA_CLIENT_ID: - required: true - CLI_TESTS_UA_CLIENT_SECRET: - required: true - CLI_TESTS_SERVICE_TOKEN: - required: true - CLI_TESTS_PROJECT_ID: - required: true - CLI_TESTS_ENV_SLUG: - required: true - CLI_TESTS_USER_EMAIL: - required: true - CLI_TESTS_USER_PASSWORD: - required: true - CLI_TESTS_INFISICAL_VAULT_FILE_PASSPHRASE: - required: true -jobs: - test: - defaults: - run: - working-directory: ./cli - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - name: Setup Go - uses: actions/setup-go@v4 - with: - go-version: "1.21.x" - - name: Install dependencies - run: go get . - - name: Test with the Go CLI - env: - CLI_TESTS_UA_CLIENT_ID: ${{ secrets.CLI_TESTS_UA_CLIENT_ID }} - CLI_TESTS_UA_CLIENT_SECRET: ${{ secrets.CLI_TESTS_UA_CLIENT_SECRET }} - CLI_TESTS_SERVICE_TOKEN: ${{ secrets.CLI_TESTS_SERVICE_TOKEN }} - CLI_TESTS_PROJECT_ID: ${{ secrets.CLI_TESTS_PROJECT_ID }} - CLI_TESTS_ENV_SLUG: ${{ secrets.CLI_TESTS_ENV_SLUG }} - CLI_TESTS_USER_EMAIL: ${{ secrets.CLI_TESTS_USER_EMAIL }} - CLI_TESTS_USER_PASSWORD: ${{ secrets.CLI_TESTS_USER_PASSWORD }} - # INFISICAL_VAULT_FILE_PASSPHRASE: ${{ secrets.CLI_TESTS_INFISICAL_VAULT_FILE_PASSPHRASE }} - - run: go test -v -count=1 ./test diff --git a/.goreleaser.yaml b/.goreleaser.yaml deleted file mode 100644 index e3147d650..000000000 --- a/.goreleaser.yaml +++ /dev/null @@ -1,241 +0,0 @@ -# This is an example .goreleaser.yml file with some sensible defaults. -# Make sure to check the documentation at https://goreleaser.com -# before: -# hooks: -# # You may remove this if you don't use go modules. -# - cd cli && go mod tidy -# # you may remove this if you don't need go generate -# - cd cli && go generate ./... -before: - hooks: - - ./cli/scripts/completions.sh - - ./cli/scripts/manpages.sh - -monorepo: - tag_prefix: infisical-cli/ - dir: cli - -builds: - - id: darwin-build - binary: infisical - 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: - - CGO_ENABLED=1 - - CC=/home/runner/work/osxcross/target/bin/o64-clang - - CXX=/home/runner/work/osxcross/target/bin/o64-clang++ - goos: - - darwin - ignore: - - goos: darwin - goarch: "386" - dir: ./cli - - - id: all-other-builds - env: - - CGO_ENABLED=0 - binary: infisical - 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: - - freebsd - - linux - - netbsd - - openbsd - - windows - goarch: - - "386" - - amd64 - - arm - - arm64 - goarm: - - "6" - - "7" - ignore: - - goos: windows - goarch: "386" - - goos: freebsd - goarch: "386" - dir: ./cli - -archives: - - format_overrides: - - goos: windows - format: zip - files: - - ../README* - - ../LICENSE* - - ../manpages/* - - ../completions/* - -release: - replace_existing_draft: true - mode: "replace" - -checksum: - name_template: "checksums.txt" - -snapshot: - name_template: "{{ .Version }}-devel" - -# publishers: -# - name: fury.io -# ids: -# - infisical -# dir: "{{ dir .ArtifactPath }}" -# cmd: curl -F package=@{{ .ArtifactName }} https://{{ .Env.FURY_TOKEN }}@push.fury.io/infisical/ - -brews: - - name: infisical - tap: - owner: Infisical - name: homebrew-get-cli - commit_author: - name: "Infisical" - email: ai@infisical.com - folder: Formula - homepage: "https://infisical.com" - description: "The official Infisical CLI" - install: |- - bin.install "infisical" - bash_completion.install "completions/infisical.bash" => "infisical" - zsh_completion.install "completions/infisical.zsh" => "_infisical" - fish_completion.install "completions/infisical.fish" - man1.install "manpages/infisical.1.gz" - - name: "infisical@{{.Version}}" - tap: - owner: Infisical - name: homebrew-get-cli - commit_author: - name: "Infisical" - email: ai@infisical.com - folder: Formula - homepage: "https://infisical.com" - description: "The official Infisical CLI" - install: |- - bin.install "infisical" - bash_completion.install "completions/infisical.bash" => "infisical" - zsh_completion.install "completions/infisical.zsh" => "_infisical" - fish_completion.install "completions/infisical.fish" - man1.install "manpages/infisical.1.gz" - -nfpms: - - id: infisical - package_name: infisical - builds: - - all-other-builds - vendor: Infisical, Inc - homepage: https://infisical.com/ - maintainer: Infisical, Inc - description: The offical Infisical CLI - license: MIT - formats: - - rpm - - deb - - apk - - archlinux - bindir: /usr/bin - contents: - - src: ./completions/infisical.bash - dst: /etc/bash_completion.d/infisical - - src: ./completions/infisical.fish - dst: /usr/share/fish/vendor_completions.d/infisical.fish - - src: ./completions/infisical.zsh - dst: /usr/share/zsh/site-functions/_infisical - - src: ./manpages/infisical.1.gz - dst: /usr/share/man/man1/infisical.1.gz - -scoop: - bucket: - owner: Infisical - name: scoop-infisical - commit_author: - name: "Infisical" - email: ai@infisical.com - homepage: "https://infisical.com" - description: "The official Infisical CLI" - license: MIT - -winget: - - name: infisical - publisher: infisical - license: MIT - homepage: https://infisical.com - short_description: "The official Infisical CLI" - repository: - owner: infisical - name: winget-pkgs - branch: "infisical-{{.Version}}" - pull_request: - enabled: true - draft: false - base: - owner: microsoft - name: winget-pkgs - branch: master - -aurs: - - name: infisical-bin - homepage: "https://infisical.com" - description: "The official Infisical CLI" - maintainers: - - Infisical, Inc - license: MIT - private_key: "{{ .Env.AUR_KEY }}" - git_url: "ssh://aur@aur.archlinux.org/infisical-bin.git" - package: |- - # bin - install -Dm755 "./infisical" "${pkgdir}/usr/bin/infisical" - # license - install -Dm644 "./LICENSE" "${pkgdir}/usr/share/licenses/infisical/LICENSE" - # completions - mkdir -p "${pkgdir}/usr/share/bash-completion/completions/" - mkdir -p "${pkgdir}/usr/share/zsh/site-functions/" - mkdir -p "${pkgdir}/usr/share/fish/vendor_completions.d/" - install -Dm644 "./completions/infisical.bash" "${pkgdir}/usr/share/bash-completion/completions/infisical" - install -Dm644 "./completions/infisical.zsh" "${pkgdir}/usr/share/zsh/site-functions/_infisical" - install -Dm644 "./completions/infisical.fish" "${pkgdir}/usr/share/fish/vendor_completions.d/infisical.fish" - # man pages - install -Dm644 "./manpages/infisical.1.gz" "${pkgdir}/usr/share/man/man1/infisical.1.gz" - -dockers: - - dockerfile: docker/alpine - goos: linux - goarch: amd64 - use: buildx - ids: - - all-other-builds - image_templates: - - "infisical/cli:{{ .Major }}.{{ .Minor }}.{{ .Patch }}-amd64" - - "infisical/cli:latest-amd64" - build_flag_templates: - - "--pull" - - "--platform=linux/amd64" - - dockerfile: docker/alpine - goos: linux - goarch: amd64 - use: buildx - ids: - - all-other-builds - image_templates: - - "infisical/cli:{{ .Major }}.{{ .Minor }}.{{ .Patch }}-arm64" - - "infisical/cli:latest-arm64" - build_flag_templates: - - "--pull" - - "--platform=linux/arm64" - -docker_manifests: - - name_template: "infisical/cli:{{ .Major }}.{{ .Minor }}.{{ .Patch }}" - image_templates: - - "infisical/cli:{{ .Major }}.{{ .Minor }}.{{ .Patch }}-amd64" - - "infisical/cli:{{ .Major }}.{{ .Minor }}.{{ .Patch }}-arm64" - - name_template: "infisical/cli:latest" - image_templates: - - "infisical/cli:latest-amd64" - - "infisical/cli:latest-arm64" diff --git a/npm/.eslintrc.json b/npm/.eslintrc.json deleted file mode 100644 index de8743bbb..000000000 --- a/npm/.eslintrc.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "env": { - "es6": true, - "node": true - }, - "parserOptions": { - "ecmaVersion": "latest" - } -} diff --git a/npm/README.md b/npm/README.md deleted file mode 100644 index febd52eec..000000000 --- a/npm/README.md +++ /dev/null @@ -1,68 +0,0 @@ -

Infisical CLI

-

-

Embrace shift-left security with the Infisical CLI and strengthen your DevSecOps practices by seamlessly managing secrets across your workflows, pipelines, and applications.

-

- -

- Slack | - Node.js SDK | - Infisical Cloud | - Self-Hosting | - Docs | - Website | - Hiring (Remote/SF) -

- -

- - Infisical is released under the MIT license. - - - PRs welcome! - - - git commit activity - - - Cloudsmith downloads - - - Slack community channel - - - Infisical Twitter - -

- -### Introduction - -The Infisical CLI is a powerful command line tool that can be used to retrieve, modify, export and inject secrets into any process or application as environment variables. You can use it across various environments, whether it’s local development, CI/CD, staging, or production. - -### Installation - -The Infisical CLI NPM package serves as a new installation method in addition to our [existing installation methods](https://infisical.com/docs/cli/overview). - -After installing the CLI with the command below, you'll be able to use the infisical CLI across your machine. - -```bash -$ npm install -g @infisical/cli -``` - -Full example: -```bash -# Install the Infisical CLI -$ npm install -g @infisical/cli - -# Authenticate with the Infisical CLI -$ infisical login - -# Initialize your Infisical CLI -$ infisical init - -# List your secrets with Infisical CLI -$ infisical secrets -``` - - -### Documentation -Our full CLI documentation can be found [here](https://infisical.com/docs/cli/usage). \ No newline at end of file diff --git a/npm/package-lock.json b/npm/package-lock.json deleted file mode 100644 index 0c3dea6ef..000000000 --- a/npm/package-lock.json +++ /dev/null @@ -1,141 +0,0 @@ -{ - "name": "@infisical/cli", - "version": "0.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@infisical/cli", - "version": "0.0.0", - "hasInstallScript": true, - "dependencies": { - "tar": "^6.2.0", - "yauzl": "^3.2.0" - }, - "bin": { - "infisical": "bin/infisical" - } - }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "license": "MIT" - }, - "node_modules/tar": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", - "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yauzl": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.2.0.tgz", - "integrity": "sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w==", - "license": "MIT", - "dependencies": { - "buffer-crc32": "~0.2.3", - "pend": "~1.2.0" - }, - "engines": { - "node": ">=12" - } - } - } -} diff --git a/npm/package.json b/npm/package.json deleted file mode 100644 index 0b9d0bbaf..000000000 --- a/npm/package.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "@infisical/cli", - "private": false, - "version": "0.0.0", - "keywords": [ - "infisical", - "cli", - "command-line" - ], - "bin": { - "infisical": "./bin/infisical" - }, - "repository": { - "type": "git", - "url": "https://github.com/Infisical/infisical.git" - }, - "author": "Infisical Inc, ", - "scripts": { - "preinstall": "node src/index.cjs" - }, - "dependencies": { - "tar": "^6.2.0", - "yauzl": "^3.2.0" - } -} diff --git a/npm/src/index.cjs b/npm/src/index.cjs deleted file mode 100644 index f1ff51069..000000000 --- a/npm/src/index.cjs +++ /dev/null @@ -1,166 +0,0 @@ -const childProcess = require("child_process"); -const fs = require("fs"); -const stream = require("node:stream"); -const tar = require("tar"); -const path = require("path"); -const zlib = require("zlib"); -const yauzl = require("yauzl"); - -const packageJSON = require("../package.json"); - -const supportedPlatforms = ["linux", "darwin", "win32", "freebsd", "windows"]; -const outputDir = "bin"; - -const getPlatform = () => { - let platform = process.platform; - - if (platform === "win32") { - platform = "windows"; - } - - if (!supportedPlatforms.includes(platform)) { - console.error("Your platform doesn't seem to be of type darwin, linux or windows"); - process.exit(1); - } - return platform; -}; - -const getArchitecture = () => { - const architecture = process.arch; - let arch = ""; - - if (architecture === "x64" || architecture === "amd64") { - arch = "amd64"; - } else if (architecture === "arm64") { - arch = "arm64"; - } else if (architecture === "arm") { - // If the platform is Linux, we should find the exact ARM version, otherwise we default to armv7 which is the most common - if (process.platform === "linux" || process.platform === "freebsd") { - const output = childProcess.execSync("uname -m").toString().trim(); - - const armVersions = ["armv5", "armv6", "armv7"]; - - const armVersion = armVersions.find(version => output.startsWith(version)); - - if (armVersion) { - arch = armVersion; - } else { - arch = "armv7"; - } - } else { - arch = "armv7"; - } - } else if (architecture === "ia32") { - arch = "i386"; - } else { - console.error("Your architecture doesn't seem to be supported. Your architecture is", architecture); - process.exit(1); - } - - return arch; -}; - -async function extractZip(buffer, targetPath) { - return new Promise((resolve, reject) => { - yauzl.fromBuffer(buffer, { lazyEntries: true }, (err, zipfile) => { - if (err) return reject(err); - - zipfile.readEntry(); - zipfile.on("entry", entry => { - const isExecutable = entry.fileName === "infisical" || entry.fileName === "infisical.exe"; - - if (/\/$/.test(entry.fileName) || !isExecutable) { - // Directory entry - zipfile.readEntry(); - } else { - // File entry - zipfile.openReadStream(entry, (err, readStream) => { - if (err) return reject(err); - - let fileName = entry.fileName; - - if (entry.fileName.endsWith(".exe")) { - fileName = "infisical.exe"; - } else if (entry.fileName.includes("infisical")) { - fileName = "infisical"; - } - - const outputPath = path.join(targetPath, fileName); - const writeStream = fs.createWriteStream(outputPath); - - readStream.pipe(writeStream); - writeStream.on("close", () => { - zipfile.readEntry(); - }); - }); - } - }); - - zipfile.on("end", resolve); - zipfile.on("error", reject); - }); - }); -} - -async function main() { - const PLATFORM = getPlatform(); - const ARCH = getArchitecture(); - const NUMERIC_RELEASE_VERSION = packageJSON.version; - const LATEST_RELEASE_VERSION = `v${NUMERIC_RELEASE_VERSION}`; - const EXTENSION = PLATFORM === "windows" ? "zip" : "tar.gz"; - const downloadLink = `https://github.com/Infisical/infisical/releases/download/infisical-cli/${LATEST_RELEASE_VERSION}/infisical_${NUMERIC_RELEASE_VERSION}_${PLATFORM}_${ARCH}.${EXTENSION}`; - - // Ensure the output directory exists - if (!fs.existsSync(outputDir)) { - fs.mkdirSync(outputDir); - } - - // Download the latest CLI binary - try { - const response = await fetch(downloadLink, { - headers: { - Accept: "application/octet-stream" - } - }); - - if (!response.ok) { - throw new Error(`Failed to fetch: ${response.status} - ${response.statusText}`); - } - - if (EXTENSION === "zip") { - // For ZIP files, we need to buffer the whole thing first - const buffer = await response.arrayBuffer(); - await extractZip(Buffer.from(buffer), outputDir); - } else { - // For tar.gz files, we stream - await new Promise((resolve, reject) => { - const outStream = stream.Readable.fromWeb(response.body) - .pipe(zlib.createGunzip()) - .pipe( - tar.x({ - C: path.join(outputDir), - filter: path => path === "infisical" - }) - ); - - outStream.on("error", reject); - outStream.on("close", resolve); - }); - } - - // Platform-specific tasks - if (PLATFORM === "windows") { - // We create an empty file called 'infisical'. This file has no functionality, except allowing NPM to correctly create the symlink. - // Reason why this doesn't work without the empty file, is because the files downloaded are a .ps1, .exe, and .cmd file. None of these match the binary name from the package.json['bin'] field. - // This is a bit hacky, but it assures that the symlink is correctly created. - fs.closeSync(fs.openSync(path.join(outputDir, "infisical"), "w")); - } else { - // Unix systems only need chmod - fs.chmodSync(path.join(outputDir, "infisical"), "755"); - } - } catch (error) { - console.error("Error downloading or extracting Infisical CLI:", error); - process.exit(1); - } -} -main();