api-key-manager 4.1.0__tar.gz → 4.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. api_key_manager-4.3.0/LICENSE +21 -0
  2. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/PKG-INFO +190 -10
  3. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/README.md +187 -9
  4. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/api_key_manager.egg-info/PKG-INFO +190 -10
  5. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/api_key_manager.egg-info/SOURCES.txt +7 -0
  6. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/__init__.py +1 -1
  7. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/api_models.py +1 -0
  8. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/config.py +1 -0
  9. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/detector.py +9 -13
  10. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/errors.py +3 -0
  11. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/storage.py +57 -10
  12. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/__init__.py +12 -0
  13. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/_app.py +23 -11
  14. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/middleware.py +20 -3
  15. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/routes/check.py +35 -0
  16. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/routes/keys.py +75 -0
  17. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/pyproject.toml +1 -1
  18. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_api_endpoints.py +129 -0
  19. api_key_manager-4.3.0/tests/test_detector_unit.py +388 -0
  20. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_errors.py +1 -0
  21. api_key_manager-4.3.0/tests/test_routes_check.py +636 -0
  22. api_key_manager-4.3.0/tests/test_routes_misc.py +386 -0
  23. api_key_manager-4.3.0/tests/test_routes_models.py +549 -0
  24. api_key_manager-4.3.0/tests/test_routes_providers.py +506 -0
  25. api_key_manager-4.3.0/tests/test_routes_test.py +813 -0
  26. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_storage.py +217 -1
  27. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_tester.py +1 -1
  28. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/api_key_manager.egg-info/dependency_links.txt +0 -0
  29. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/api_key_manager.egg-info/entry_points.txt +0 -0
  30. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/api_key_manager.egg-info/requires.txt +0 -0
  31. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/api_key_manager.egg-info/top_level.txt +0 -0
  32. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/__main__.py +0 -0
  33. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/checker.py +0 -0
  34. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/cli.py +0 -0
  35. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/core.py +0 -0
  36. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/i18n.py +0 -0
  37. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/logger.py +0 -0
  38. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/model_capabilities.py +0 -0
  39. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/parser.py +0 -0
  40. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/__init__.py +0 -0
  41. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/ai302.py +0 -0
  42. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/anthropic.py +0 -0
  43. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/baichuan.py +0 -0
  44. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/base.py +0 -0
  45. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/cerebras.py +0 -0
  46. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/cohere.py +0 -0
  47. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/config_providers.py +0 -0
  48. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/cstcloud.py +0 -0
  49. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/dashscope.py +0 -0
  50. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/dashscope_coding.py +0 -0
  51. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/deepseek.py +0 -0
  52. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/dmxapi.py +0 -0
  53. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/doubao.py +0 -0
  54. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/fireworks.py +0 -0
  55. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/google.py +0 -0
  56. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/grok.py +0 -0
  57. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/groq.py +0 -0
  58. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/huggingface.py +0 -0
  59. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/hyperbolic.py +0 -0
  60. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/infini.py +0 -0
  61. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/infini_coding.py +0 -0
  62. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/kimi.py +0 -0
  63. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/kimi_coding.py +0 -0
  64. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/longcat.py +0 -0
  65. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/mimo.py +0 -0
  66. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/mimo_plan.py +0 -0
  67. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/minimax.py +0 -0
  68. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/minimax_plan.py +0 -0
  69. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/mistral.py +0 -0
  70. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/models_registry.py +0 -0
  71. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/modelscope.py +0 -0
  72. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/nvidia.py +0 -0
  73. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/ocoolai.py +0 -0
  74. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/openai.py +0 -0
  75. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/opencode.py +0 -0
  76. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/opencode_zen.py +0 -0
  77. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/openrouter.py +0 -0
  78. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/perplexity.py +0 -0
  79. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/poe.py +0 -0
  80. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/ppio.py +0 -0
  81. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/replicate.py +0 -0
  82. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/siliconflow.py +0 -0
  83. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/stepfun.py +0 -0
  84. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/tencent_hunyuan.py +0 -0
  85. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/together.py +0 -0
  86. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/yi.py +0 -0
  87. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/zai.py +0 -0
  88. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/zhipu.py +0 -0
  89. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/zhipu_coding.py +0 -0
  90. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/proxy.py +0 -0
  91. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/py.typed +0 -0
  92. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/ssrf.py +0 -0
  93. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/tester.py +0 -0
  94. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/url_override.py +0 -0
  95. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/validator.py +0 -0
  96. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/progress.py +0 -0
  97. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/routes/__init__.py +0 -0
  98. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/routes/balance.py +0 -0
  99. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/routes/misc.py +0 -0
  100. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/routes/models.py +0 -0
  101. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/routes/providers.py +0 -0
  102. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/routes/stats.py +0 -0
  103. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/routes/test.py +0 -0
  104. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/utils.py +0 -0
  105. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/webhook.py +0 -0
  106. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/setup.cfg +0 -0
  107. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_base_check.py +0 -0
  108. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_bug_fixes.py +0 -0
  109. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_checker.py +0 -0
  110. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_core.py +0 -0
  111. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_deepseek_balance.py +0 -0
  112. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_e2e.py +0 -0
  113. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_i18n.py +0 -0
  114. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_logger.py +0 -0
  115. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_openapi.py +0 -0
  116. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_parser.py +0 -0
  117. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_provider_detection.py +0 -0
  118. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_provider_refactoring.py +0 -0
  119. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_providers.py +0 -0
  120. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_proxy.py +0 -0
  121. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_security.py +0 -0
  122. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_token_limit_fast.py +0 -0
  123. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_v320_changes.py +0 -0
  124. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_validator.py +0 -0
  125. {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_webhook.py +0 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Townrain
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: api-key-manager
3
- Version: 4.1.0
3
+ Version: 4.3.0
4
4
  Summary: Batch manage API keys for 45+ AI providers with CLI and Web interfaces
5
5
  Author: Townrain
6
6
  License: MIT
@@ -18,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.12
18
18
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
19
  Requires-Python: >=3.10
20
20
  Description-Content-Type: text/markdown
21
+ License-File: LICENSE
21
22
  Requires-Dist: httpx>=0.27.2
22
23
  Requires-Dist: pyyaml>=6.0.2
23
24
  Requires-Dist: rich>=13.9.0
@@ -32,6 +33,7 @@ Requires-Dist: pytest>=8.0.0; extra == "dev"
32
33
  Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
33
34
  Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
34
35
  Requires-Dist: ruff>=0.4.0; extra == "dev"
36
+ Dynamic: license-file
35
37
 
36
38
  # API Key Manager
37
39
 
@@ -237,7 +239,7 @@ python web.py
237
239
 
238
240
  ### 加密存储
239
241
 
240
- API 密钥默认使用 AES-256-GCM 加密存储,每次加密使用随机盐值:
242
+ API 密钥支持 AES-256-GCM 加密存储(默认开启),每次加密使用随机盐值:
241
243
 
242
244
  ```bash
243
245
  # 设置加密密钥(环境变量)
@@ -257,6 +259,23 @@ python web.py
257
259
  }
258
260
  ```
259
261
 
262
+ #### 可选加密开关
263
+
264
+ 从 v4.2.0 起,支持通过配置关闭加密,以明文存储密钥(便于本地开发):
265
+
266
+ ```yaml
267
+ # config.yaml
268
+ storage:
269
+ keys_file: "./data/keys.json"
270
+ encrypted: false # 明文存储(本地开发推荐)
271
+ # encrypted: true # 加密存储(生产环境推荐,默认值)
272
+ ```
273
+
274
+ **说明:**
275
+ - `encrypted: true`(默认)— 使用 AES-256-GCM 加密,需设置 `KEY_MANAGER_SECRET`
276
+ - `encrypted: false` — 明文 JSON 存储,无需配置密钥,便于调试和迁移
277
+ - `load()` 自动检测文件格式,无论配置如何均可读取已有文件
278
+
260
279
  ### 安全防护
261
280
 
262
281
  - **路径遍历防护** - 导入端点验证路径在允许目录内
@@ -747,6 +766,11 @@ test:
747
766
  - 10
748
767
  - 20
749
768
 
769
+ # 存储设置
770
+ storage:
771
+ keys_file: "./data/keys.json"
772
+ encrypted: true # 设为 false 可关闭加密,以明文存储(便于本地开发)
773
+
750
774
  # 认证设置
751
775
  auth:
752
776
  api_key: "your-secret-api-key" # API 认证
@@ -840,26 +864,28 @@ python -m pytest tests/ --cov=key_manager --cov-report=term-missing
840
864
 
841
865
  | 模块 | 测试文件 | 测试数 |
842
866
  |------|---------|--------|
843
- | 提供商检测 | `test_detector.py` | 44 |
844
- | 密钥解析 | `test_parser.py` | 24 |
845
- | 验证器 | `test_validator.py` | 13 |
846
- | 检查器 | `test_checker.py` | 4 |
867
+ | 检测逻辑(单元) | `test_detector_unit.py` | 31 |
868
+ | 检测逻辑(端点) | `test_provider_detection.py` | |
869
+ | 测试路由 | `test_routes_test.py` | 48 |
870
+ | 提供商路由 | `test_routes_providers.py` | 32 |
871
+ | 杂项路由 | `test_routes_misc.py` | 26 |
872
+ | 模型路由 | `test_routes_models.py` | 34 |
873
+ | 验证路由 | `test_routes_check.py` | 23 |
847
874
  | 提供商合约 | `test_providers.py` | 220 |
875
+ | 密钥解析 | `test_parser.py` | 24 |
848
876
  | 安全回归 | `test_security.py` | 21 |
849
877
  | 加密存储 | `test_storage.py` | 26 |
850
878
  | 错误系统 | `test_errors.py` | 28 |
851
879
  | 国际化 | `test_i18n.py` | 37 |
852
- | 端到端 | `test_e2e.py` | 18 |
853
880
  | Webhook | `test_webhook.py` | 35 |
854
- | OpenAPI | `test_openapi.py` | 26 |
881
+ | 端到端 | `test_e2e.py` | 18 |
855
882
  | 代理检测 | `test_proxy.py` | 19 |
856
883
  | 日志系统 | `test_logger.py` | 21 |
857
- | 能力测试 | `test_tester.py` | 11 |
858
884
  | 核心门面 | `test_core.py` | 22 |
859
885
  | Bug 修复 | `test_bug_fixes.py` | 15 |
860
886
  | 提供商重构 | `test_provider_refactoring.py` | 32 |
861
887
 
862
- **总测试数**: 728+ | **覆盖率**: 80%
888
+ **总测试数**: 913 | **覆盖率**: 92.09%
863
889
  ## SDK 使用
864
890
 
865
891
  ### Python SDK
@@ -946,10 +972,164 @@ const result = await client.checkSingle({ key: 'sk-xxx', provider: 'openai' });
946
972
 
947
973
  模型列表从 Cherry Studio 同步,每日更新一次。新发布的模型可能需要等待同步后才能被检测到。
948
974
 
975
+ ## 预期的设计决策(安全审查说明)
976
+
977
+ > **重要**:以下设计决策是**有意为之**,不是安全漏洞。安全审查工具可能会标记这些行为,但它们是本项目作为**本地API密钥管理工具**的合理设计。
978
+
979
+ ### 1. 认证默认禁用
980
+
981
+ **行为**:当未配置 `api_key` 或 `KEY_MANAGER_API_KEY` 时,所有API端点无需认证即可访问。
982
+
983
+ **原因**:这是本地开发工具,首次运行时用户可能尚未配置认证。系统会自动从加密密钥派生API token,确保在有加密密钥的情况下仍能提供基本安全。
984
+
985
+ **配置方式**:
986
+ ```bash
987
+ # 方式1: 环境变量
988
+ set KEY_MANAGER_API_KEY=your-api-key
989
+
990
+ # 方式2: config.yaml
991
+ auth:
992
+ api_key: "your-api-key"
993
+ ```
994
+
995
+ ### 2. API Token注入到HTML
996
+
997
+ **行为**:`window.__API_TOKEN__` 被注入到 `templates/index.html` 的 `<head>` 中。
998
+
999
+ **原因**:前端JavaScript需要调用API,必须携带认证token。这是单页应用的标准做法,确保前端能自动携带token而无需用户手动配置。
1000
+
1001
+ **安全说明**:
1002
+ - Token从加密密钥派生,不是明文密码
1003
+ - 仅在本地访问时暴露(`localhost:18001`)
1004
+ - 生产环境应配置反向代理和HTTPS
1005
+
1006
+ ### 3. 完整密钥检索端点
1007
+
1008
+ **行为**:`POST /api/keys/get-full-key` 返回未掩码的完整API密钥。
1009
+
1010
+ **原因**:用户需要复制完整密钥用于其他应用。这是密钥管理工具的核心功能。
1011
+
1012
+ **安全措施**:
1013
+ - 需要认证(Bearer token)
1014
+ - 通过 `key_masked` 查询,不直接暴露密钥列表
1015
+ - 记录审计日志
1016
+
1017
+ ### 4. 速率限制存储无界增长
1018
+
1019
+ **行为**:`_RATE_LIMIT_STORE` 字典在内存中存储所有IP的请求记录,无最大条目限制。
1020
+
1021
+ **原因**:这是本地工具,通常只有少量客户端访问。DDoS攻击场景不现实(内部使用)。
1022
+
1023
+ **缓解措施**:
1024
+ - 每60秒清理超过5分钟不活跃的IP
1025
+ - 可通过 `rate_limit.requests_per_minute: 0` 禁用速率限制
1026
+
1027
+ ### 5. 中间件执行顺序
1028
+
1029
+ **行为**:中间件顺序为 `rate_limit → auth → i18n`,未认证请求会消耗速率限制配额。
1030
+
1031
+ **原因**:这是内部工具,攻击者场景不现实。顺序调整对正常使用几乎无影响。
1032
+
1033
+ **说明**:即使是未认证请求,也应该被速率限制,防止意外的大量请求(如前端bug导致的循环请求)。
1034
+
1035
+ ### 6. 文件不存在时返回空数据
1036
+
1037
+ **行为**:当 `keys.json` 文件不存在时,`_load_keys_data()` 返回 `{"keys": {}}` 而不是抛出异常。
1038
+
1039
+ **原因**:首次运行时,密钥文件尚未创建,这是有效状态。系统应优雅处理这种情况,而不是崩溃。
1040
+
1041
+ **实现**:在调用 `KeyStore.load()` 前检查文件是否存在:
1042
+ ```python
1043
+ def _load_keys_data(config_override: dict | None = None) -> dict:
1044
+ cfg = config_override or config
1045
+ keys_path = Path(cfg["storage"]["keys_file"])
1046
+ if not keys_path.exists():
1047
+ return {"keys": {}}
1048
+ # ... 继原有逻辑
1049
+ ```
1050
+
1051
+ ---
949
1052
 
950
1053
  ## 更新日志
951
1054
 
952
1055
 
1056
+ ### v4.3.0 (2026-06-24)
1057
+
1058
+ - **测试覆盖率大幅提升**: 覆盖率从 79.15% 提升至 92.09%,新增 167 个测试
1059
+ - `detector.py`: 60% → 96% — 检测逻辑全覆盖,防止 v4.2.1 类 bug 回归
1060
+ - `web/routes/test.py`: 41% → 99% — 测试路由全覆盖
1061
+ - `web/routes/providers.py`: 37% → 100% — 提供商 CRUD 全覆盖
1062
+ - `web/routes/misc.py`: 53% → 100% — Webhook/日志/签名报告全覆盖
1063
+ - `web/routes/models.py`: 72% → 97% — 模型端点全覆盖
1064
+ - `web/routes/check.py`: 74% → 97% — 验证端点全覆盖
1065
+
1066
+ - **新增测试文件**:
1067
+ - `test_detector_unit.py` (31 tests) — 检测逻辑单元测试,覆盖 7 条核心路径
1068
+ - `test_routes_test.py` (48 tests) — 测试路由端点测试
1069
+ - `test_routes_providers.py` (32 tests) — 提供商 CRUD 路由测试
1070
+ - `test_routes_misc.py` (26 tests) — Webhook/日志/签名报告路由测试
1071
+ - `test_routes_models.py` (34 tests) — 模型列表/能力/SSE 路由测试
1072
+ - `test_routes_check.py` (23 tests) — 验证/SSRF/自动保存路由测试
1073
+
1074
+ - **检测逻辑回归测试**:
1075
+ - 锁定 `detect_provider()` 全部 7 条分支路径
1076
+ - 覆盖 v4.2.1 修复的两个 bug(格式匹配跳过验证、多候选直接返回)
1077
+ - 覆盖 402 余额不足识别、签名匹配阈值、超时处理等边界情况
1078
+
1079
+ - **Bug 修复**:
1080
+ - 修复 `test_tester.py` 中 `test_run_test_progress_callback` 缺失 `test_config` fixture 参数
1081
+
1082
+
1083
+ ### v4.2.1 (2026-06-23)
1084
+
1085
+ - **检测逻辑修复**:
1086
+ - 修复格式匹配跳过验证的BUG:当有多个候选者时,不再直接返回,而是继续进行并发探测验证
1087
+ - 修复变量命名误导:`is_valid` 重命名为 `got_models`
1088
+ - 清理死代码:删除不可达的500分加分逻辑
1089
+
1090
+ - **删除/复制API认证修复**:
1091
+ - 修复 `deleteKey()` 和 `copyKey()` 使用原生 `fetch()` 导致认证失败的问题
1092
+ - 改为使用 `safeFetch()` 自动携带 Authorization header
1093
+
1094
+ ### v4.2.0 (2026-06-23)
1095
+
1096
+ - **统一认证系统**: 复用加密密钥作为API认证token
1097
+ - 新增 `derive_api_token()` 函数,从加密密钥派生API token
1098
+ - 使用独立的 salt(`key-manager-api-auth-token-v1`)避免与存储加密交叉
1099
+ - PBKDF2HMAC 600K迭代,32字节token,缓存机制
1100
+ - 修改 `auth_middleware()` 自动fallback到派生token
1101
+ - 扩展认证白名单,静态文件不需要认证
1102
+
1103
+ - **前端自动携带token**: 无需手动配置
1104
+ - 在 `templates/index.html` 的 `<head>` 中注入 `window.__API_TOKEN__`
1105
+ - 修改 `safeFetch()` 自动添加 `Authorization: Bearer <token>` 头
1106
+ - 修改 `models.js` 的SSE请求也携带token
1107
+ - 在 `state.js` 中添加 `apiToken` 字段
1108
+
1109
+ - **单个密钥删除功能**:
1110
+ - 新增 `POST /api/keys/delete` API端点
1111
+ - 前端添加删除按钮(垃圾桶图标)
1112
+ - 使用项目标准的 `showConfirm` 确认框
1113
+
1114
+ - **自动保存检测密钥**:
1115
+ - 检测密钥后自动保存到注册表
1116
+ - 更新密钥状态和检测记录
1117
+
1118
+ - **复制完整密钥功能**:
1119
+ - 新增 `POST /api/keys/get-full-key` API端点
1120
+ - 前端点击密钥可复制完整密钥
1121
+
1122
+ - **测试验证**:
1123
+ - 全量测试通过:687 passed, 1 skipped
1124
+ - 覆盖率:75.72%(超过 60% 要求)
1125
+ - 新增 5 个 `derive_api_token()` 单元测试
1126
+
1127
+ - **可选加密开关**: 支持明文存储,便于本地开发
1128
+ - 新增 `storage.encrypted` 配置项(默认 `true`)
1129
+ - 设置 `encrypted: false` 可关闭加密,以明文 JSON 存储密钥
1130
+ - `load()` 自动检测文件格式,兼容已有加密/明文文件
1131
+ - 修复 `_load_keys_data()` 解密失败时抛出异常而非返回空字典(H1 修复)
1132
+
953
1133
  ### v4.1.0 (2026-06-22)
954
1134
 
955
1135
  - **Web 模块技术债务优化**:
@@ -202,7 +202,7 @@ python web.py
202
202
 
203
203
  ### 加密存储
204
204
 
205
- API 密钥默认使用 AES-256-GCM 加密存储,每次加密使用随机盐值:
205
+ API 密钥支持 AES-256-GCM 加密存储(默认开启),每次加密使用随机盐值:
206
206
 
207
207
  ```bash
208
208
  # 设置加密密钥(环境变量)
@@ -222,6 +222,23 @@ python web.py
222
222
  }
223
223
  ```
224
224
 
225
+ #### 可选加密开关
226
+
227
+ 从 v4.2.0 起,支持通过配置关闭加密,以明文存储密钥(便于本地开发):
228
+
229
+ ```yaml
230
+ # config.yaml
231
+ storage:
232
+ keys_file: "./data/keys.json"
233
+ encrypted: false # 明文存储(本地开发推荐)
234
+ # encrypted: true # 加密存储(生产环境推荐,默认值)
235
+ ```
236
+
237
+ **说明:**
238
+ - `encrypted: true`(默认)— 使用 AES-256-GCM 加密,需设置 `KEY_MANAGER_SECRET`
239
+ - `encrypted: false` — 明文 JSON 存储,无需配置密钥,便于调试和迁移
240
+ - `load()` 自动检测文件格式,无论配置如何均可读取已有文件
241
+
225
242
  ### 安全防护
226
243
 
227
244
  - **路径遍历防护** - 导入端点验证路径在允许目录内
@@ -712,6 +729,11 @@ test:
712
729
  - 10
713
730
  - 20
714
731
 
732
+ # 存储设置
733
+ storage:
734
+ keys_file: "./data/keys.json"
735
+ encrypted: true # 设为 false 可关闭加密,以明文存储(便于本地开发)
736
+
715
737
  # 认证设置
716
738
  auth:
717
739
  api_key: "your-secret-api-key" # API 认证
@@ -805,26 +827,28 @@ python -m pytest tests/ --cov=key_manager --cov-report=term-missing
805
827
 
806
828
  | 模块 | 测试文件 | 测试数 |
807
829
  |------|---------|--------|
808
- | 提供商检测 | `test_detector.py` | 44 |
809
- | 密钥解析 | `test_parser.py` | 24 |
810
- | 验证器 | `test_validator.py` | 13 |
811
- | 检查器 | `test_checker.py` | 4 |
830
+ | 检测逻辑(单元) | `test_detector_unit.py` | 31 |
831
+ | 检测逻辑(端点) | `test_provider_detection.py` | |
832
+ | 测试路由 | `test_routes_test.py` | 48 |
833
+ | 提供商路由 | `test_routes_providers.py` | 32 |
834
+ | 杂项路由 | `test_routes_misc.py` | 26 |
835
+ | 模型路由 | `test_routes_models.py` | 34 |
836
+ | 验证路由 | `test_routes_check.py` | 23 |
812
837
  | 提供商合约 | `test_providers.py` | 220 |
838
+ | 密钥解析 | `test_parser.py` | 24 |
813
839
  | 安全回归 | `test_security.py` | 21 |
814
840
  | 加密存储 | `test_storage.py` | 26 |
815
841
  | 错误系统 | `test_errors.py` | 28 |
816
842
  | 国际化 | `test_i18n.py` | 37 |
817
- | 端到端 | `test_e2e.py` | 18 |
818
843
  | Webhook | `test_webhook.py` | 35 |
819
- | OpenAPI | `test_openapi.py` | 26 |
844
+ | 端到端 | `test_e2e.py` | 18 |
820
845
  | 代理检测 | `test_proxy.py` | 19 |
821
846
  | 日志系统 | `test_logger.py` | 21 |
822
- | 能力测试 | `test_tester.py` | 11 |
823
847
  | 核心门面 | `test_core.py` | 22 |
824
848
  | Bug 修复 | `test_bug_fixes.py` | 15 |
825
849
  | 提供商重构 | `test_provider_refactoring.py` | 32 |
826
850
 
827
- **总测试数**: 728+ | **覆盖率**: 80%
851
+ **总测试数**: 913 | **覆盖率**: 92.09%
828
852
  ## SDK 使用
829
853
 
830
854
  ### Python SDK
@@ -911,10 +935,164 @@ const result = await client.checkSingle({ key: 'sk-xxx', provider: 'openai' });
911
935
 
912
936
  模型列表从 Cherry Studio 同步,每日更新一次。新发布的模型可能需要等待同步后才能被检测到。
913
937
 
938
+ ## 预期的设计决策(安全审查说明)
939
+
940
+ > **重要**:以下设计决策是**有意为之**,不是安全漏洞。安全审查工具可能会标记这些行为,但它们是本项目作为**本地API密钥管理工具**的合理设计。
941
+
942
+ ### 1. 认证默认禁用
943
+
944
+ **行为**:当未配置 `api_key` 或 `KEY_MANAGER_API_KEY` 时,所有API端点无需认证即可访问。
945
+
946
+ **原因**:这是本地开发工具,首次运行时用户可能尚未配置认证。系统会自动从加密密钥派生API token,确保在有加密密钥的情况下仍能提供基本安全。
947
+
948
+ **配置方式**:
949
+ ```bash
950
+ # 方式1: 环境变量
951
+ set KEY_MANAGER_API_KEY=your-api-key
952
+
953
+ # 方式2: config.yaml
954
+ auth:
955
+ api_key: "your-api-key"
956
+ ```
957
+
958
+ ### 2. API Token注入到HTML
959
+
960
+ **行为**:`window.__API_TOKEN__` 被注入到 `templates/index.html` 的 `<head>` 中。
961
+
962
+ **原因**:前端JavaScript需要调用API,必须携带认证token。这是单页应用的标准做法,确保前端能自动携带token而无需用户手动配置。
963
+
964
+ **安全说明**:
965
+ - Token从加密密钥派生,不是明文密码
966
+ - 仅在本地访问时暴露(`localhost:18001`)
967
+ - 生产环境应配置反向代理和HTTPS
968
+
969
+ ### 3. 完整密钥检索端点
970
+
971
+ **行为**:`POST /api/keys/get-full-key` 返回未掩码的完整API密钥。
972
+
973
+ **原因**:用户需要复制完整密钥用于其他应用。这是密钥管理工具的核心功能。
974
+
975
+ **安全措施**:
976
+ - 需要认证(Bearer token)
977
+ - 通过 `key_masked` 查询,不直接暴露密钥列表
978
+ - 记录审计日志
979
+
980
+ ### 4. 速率限制存储无界增长
981
+
982
+ **行为**:`_RATE_LIMIT_STORE` 字典在内存中存储所有IP的请求记录,无最大条目限制。
983
+
984
+ **原因**:这是本地工具,通常只有少量客户端访问。DDoS攻击场景不现实(内部使用)。
985
+
986
+ **缓解措施**:
987
+ - 每60秒清理超过5分钟不活跃的IP
988
+ - 可通过 `rate_limit.requests_per_minute: 0` 禁用速率限制
989
+
990
+ ### 5. 中间件执行顺序
991
+
992
+ **行为**:中间件顺序为 `rate_limit → auth → i18n`,未认证请求会消耗速率限制配额。
993
+
994
+ **原因**:这是内部工具,攻击者场景不现实。顺序调整对正常使用几乎无影响。
995
+
996
+ **说明**:即使是未认证请求,也应该被速率限制,防止意外的大量请求(如前端bug导致的循环请求)。
997
+
998
+ ### 6. 文件不存在时返回空数据
999
+
1000
+ **行为**:当 `keys.json` 文件不存在时,`_load_keys_data()` 返回 `{"keys": {}}` 而不是抛出异常。
1001
+
1002
+ **原因**:首次运行时,密钥文件尚未创建,这是有效状态。系统应优雅处理这种情况,而不是崩溃。
1003
+
1004
+ **实现**:在调用 `KeyStore.load()` 前检查文件是否存在:
1005
+ ```python
1006
+ def _load_keys_data(config_override: dict | None = None) -> dict:
1007
+ cfg = config_override or config
1008
+ keys_path = Path(cfg["storage"]["keys_file"])
1009
+ if not keys_path.exists():
1010
+ return {"keys": {}}
1011
+ # ... 继原有逻辑
1012
+ ```
1013
+
1014
+ ---
914
1015
 
915
1016
  ## 更新日志
916
1017
 
917
1018
 
1019
+ ### v4.3.0 (2026-06-24)
1020
+
1021
+ - **测试覆盖率大幅提升**: 覆盖率从 79.15% 提升至 92.09%,新增 167 个测试
1022
+ - `detector.py`: 60% → 96% — 检测逻辑全覆盖,防止 v4.2.1 类 bug 回归
1023
+ - `web/routes/test.py`: 41% → 99% — 测试路由全覆盖
1024
+ - `web/routes/providers.py`: 37% → 100% — 提供商 CRUD 全覆盖
1025
+ - `web/routes/misc.py`: 53% → 100% — Webhook/日志/签名报告全覆盖
1026
+ - `web/routes/models.py`: 72% → 97% — 模型端点全覆盖
1027
+ - `web/routes/check.py`: 74% → 97% — 验证端点全覆盖
1028
+
1029
+ - **新增测试文件**:
1030
+ - `test_detector_unit.py` (31 tests) — 检测逻辑单元测试,覆盖 7 条核心路径
1031
+ - `test_routes_test.py` (48 tests) — 测试路由端点测试
1032
+ - `test_routes_providers.py` (32 tests) — 提供商 CRUD 路由测试
1033
+ - `test_routes_misc.py` (26 tests) — Webhook/日志/签名报告路由测试
1034
+ - `test_routes_models.py` (34 tests) — 模型列表/能力/SSE 路由测试
1035
+ - `test_routes_check.py` (23 tests) — 验证/SSRF/自动保存路由测试
1036
+
1037
+ - **检测逻辑回归测试**:
1038
+ - 锁定 `detect_provider()` 全部 7 条分支路径
1039
+ - 覆盖 v4.2.1 修复的两个 bug(格式匹配跳过验证、多候选直接返回)
1040
+ - 覆盖 402 余额不足识别、签名匹配阈值、超时处理等边界情况
1041
+
1042
+ - **Bug 修复**:
1043
+ - 修复 `test_tester.py` 中 `test_run_test_progress_callback` 缺失 `test_config` fixture 参数
1044
+
1045
+
1046
+ ### v4.2.1 (2026-06-23)
1047
+
1048
+ - **检测逻辑修复**:
1049
+ - 修复格式匹配跳过验证的BUG:当有多个候选者时,不再直接返回,而是继续进行并发探测验证
1050
+ - 修复变量命名误导:`is_valid` 重命名为 `got_models`
1051
+ - 清理死代码:删除不可达的500分加分逻辑
1052
+
1053
+ - **删除/复制API认证修复**:
1054
+ - 修复 `deleteKey()` 和 `copyKey()` 使用原生 `fetch()` 导致认证失败的问题
1055
+ - 改为使用 `safeFetch()` 自动携带 Authorization header
1056
+
1057
+ ### v4.2.0 (2026-06-23)
1058
+
1059
+ - **统一认证系统**: 复用加密密钥作为API认证token
1060
+ - 新增 `derive_api_token()` 函数,从加密密钥派生API token
1061
+ - 使用独立的 salt(`key-manager-api-auth-token-v1`)避免与存储加密交叉
1062
+ - PBKDF2HMAC 600K迭代,32字节token,缓存机制
1063
+ - 修改 `auth_middleware()` 自动fallback到派生token
1064
+ - 扩展认证白名单,静态文件不需要认证
1065
+
1066
+ - **前端自动携带token**: 无需手动配置
1067
+ - 在 `templates/index.html` 的 `<head>` 中注入 `window.__API_TOKEN__`
1068
+ - 修改 `safeFetch()` 自动添加 `Authorization: Bearer <token>` 头
1069
+ - 修改 `models.js` 的SSE请求也携带token
1070
+ - 在 `state.js` 中添加 `apiToken` 字段
1071
+
1072
+ - **单个密钥删除功能**:
1073
+ - 新增 `POST /api/keys/delete` API端点
1074
+ - 前端添加删除按钮(垃圾桶图标)
1075
+ - 使用项目标准的 `showConfirm` 确认框
1076
+
1077
+ - **自动保存检测密钥**:
1078
+ - 检测密钥后自动保存到注册表
1079
+ - 更新密钥状态和检测记录
1080
+
1081
+ - **复制完整密钥功能**:
1082
+ - 新增 `POST /api/keys/get-full-key` API端点
1083
+ - 前端点击密钥可复制完整密钥
1084
+
1085
+ - **测试验证**:
1086
+ - 全量测试通过:687 passed, 1 skipped
1087
+ - 覆盖率:75.72%(超过 60% 要求)
1088
+ - 新增 5 个 `derive_api_token()` 单元测试
1089
+
1090
+ - **可选加密开关**: 支持明文存储,便于本地开发
1091
+ - 新增 `storage.encrypted` 配置项(默认 `true`)
1092
+ - 设置 `encrypted: false` 可关闭加密,以明文 JSON 存储密钥
1093
+ - `load()` 自动检测文件格式,兼容已有加密/明文文件
1094
+ - 修复 `_load_keys_data()` 解密失败时抛出异常而非返回空字典(H1 修复)
1095
+
918
1096
  ### v4.1.0 (2026-06-22)
919
1097
 
920
1098
  - **Web 模块技术债务优化**: