From d47f6f7ec999a6fc3c2e9ef1f731ae3abb2d8bd5 Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Tue, 29 Jul 2025 20:49:54 +0800 Subject: [PATCH] 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 @@ -xU1 0`ܯ8nJ.:*(8\ɕ$ w3 Nox{$6f1~wF'0YbF TBpND|*]uCST 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 @@ -xM -0F]s%t -"I2E۔ >x_2w@;z㑈ءCX@6 5)M&F:l'FTHďFF1iPSm4cNo;ݷV{]ߗT`=aZw d}fuKK \ 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 @@ -xU1 0`ܯ8nJ.:*(8\ɕ$ w3 Nox{$6f1~wF'0YbF TBpND|*]uCST 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 @@ -xM -0F]s%t -"I2E۔ >x_2w@;z㑈ءCX@6 5)M&F:l'FTHďFF1iPSm4cNo;ݷV{]ߗT`=aZw d}fuKK \ 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_uQ;rS/jozH[&Yu;OM;;fhtXؠ?Ϻ[_]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