From bf8c7c61c30b19bfe7c632a2fe33681f517dab09 Mon Sep 17 00:00:00 2001 From: yf-yang Date: Tue, 2 Jun 2026 11:51:17 +0800 Subject: [PATCH] fix: hide ak sk info from tccli configure list outputs --- tccli/configure.py | 27 ++++++++++++++++++++--- tests/test_configure.py | 49 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 tests/test_configure.py diff --git a/tccli/configure.py b/tccli/configure.py index 4881678c1d..6c4150c6c5 100644 --- a/tccli/configure.py +++ b/tccli/configure.py @@ -118,8 +118,8 @@ class ConfigureListCommand(BasicConfigure): USEAGE = 'tccli configure list [--profile profile-name]' EXAMPLES = "$ tccli configure list\n" \ "credential:\n" \ - "secretId = ********************************\n" \ - "secretKey = ********************************\n" \ + "secretId = AKID************************1234\n" \ + "secretKey = abcd************************5678\n" \ "configure:\n" \ "region = ap-guangzhou\n" \ "output = json\n" \ @@ -128,11 +128,32 @@ class ConfigureListCommand(BasicConfigure): "cvm.endpoint = cvm.tencentcloudapi.com\n" \ "...\n" \ "..." + SENSITIVE_CREDENTIALS = [OptionsDefine.SecretId, OptionsDefine.SecretKey] + SECRET_VISIBLE_CHARS = 4 def __init__(self, stream=sys.stdout): super(ConfigureListCommand, self).__init__() self._stream = stream + @classmethod + def _mask_sensitive_credential(cls, value): + if not isinstance(value, six.string_types): + value = str(value) + + visible_chars = cls.SECRET_VISIBLE_CHARS + if len(value) <= visible_chars * 2: + return "*" * len(value) + return "%s%s%s" % ( + value[:visible_chars], + "*" * (len(value) - visible_chars * 2), + value[-visible_chars:] + ) + + def _format_credential_value(self, config, value): + if config in self.SENSITIVE_CREDENTIALS: + return self._mask_sensitive_credential(value) + return value + def _run_main(self, args, parsed_globals): profile_name = self._get_profile_name(parsed_globals) @@ -142,7 +163,7 @@ def _run_main(self, args, parsed_globals): cred = Utils.load_json_msg(cred_path) for config in self.cred_list: if config in cred and cred[config]: - self._stream.write("%s = %s\n" % (config, cred[config])) + self._stream.write("%s = %s\n" % (config, self._format_credential_value(config, cred[config]))) # other in x.configure is_exit, config_path = self._profile_existed(profile_name + ".configure") diff --git a/tests/test_configure.py b/tests/test_configure.py new file mode 100644 index 0000000000..c05ec1825f --- /dev/null +++ b/tests/test_configure.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +try: + from io import StringIO +except ImportError: + from StringIO import StringIO + +import argparse + +from tccli.configure import ConfigureListCommand +from tccli.utils import Utils + + +def test_configure_list_masks_secret_id_and_secret_key(tmpdir): + cli_path = str(tmpdir) + Utils.dump_json_msg( + tmpdir.join("default.credential").strpath, + { + "secretId": "AKID1234567890SECRETID", + "secretKey": "SECRETKEY1234567890VALUE", + "token": "plain-token", + } + ) + Utils.dump_json_msg( + tmpdir.join("default.configure").strpath, + { + "_sys_param": { + "region": "ap-guangzhou", + "output": "json", + } + } + ) + + stream = StringIO() + command = ConfigureListCommand(stream=stream) + command.cli_path = cli_path + command._run_main(None, argparse.Namespace(profile="default")) + + output = stream.getvalue() + assert "secretId = AKID**************ETID" in output + assert "secretKey = SECR****************ALUE" in output + assert "AKID1234567890SECRETID" not in output + assert "SECRETKEY1234567890VALUE" not in output + assert "token = plain-token" in output + assert "region = ap-guangzhou" in output + + +def test_configure_list_fully_masks_short_secrets(): + assert ConfigureListCommand._mask_sensitive_credential("12345678") == "********" + assert ConfigureListCommand._mask_sensitive_credential("1234567") == "*******"