mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-31 08:25:35 +00:00
fix(security): 屏蔽 admin 账号接口返回的敏感凭证字段
Account.Credentials 是 JSONB map,混合存放可编辑的非敏感配置(base_url、 model_mapping、project_id 等)与敏感秘钥(OAuth access/refresh/id token、 API key、AWS secret、Vertex private key 等)。当前所有 admin 账号接口直接 透传该 map,token 经由浏览器 DevTools、抓包、日志等途径泄漏。 - service 包新增 SensitiveCredentialKeys 清单与 MergePreservingSensitiveCreds 作为单一权威定义。 - dto 层 RedactCredentials 在响应里剥离敏感子键,输出 credentials_status (has_<key> 布尔标识)告知前端存在性,不暴露原值。 - AccountFromServiceShallow 接入脱敏,覆盖 list、get、create、update、 refresh、batch、bulk-update、OAuth 创建等 9 个 handler。 - service.UpdateAccount 改为合并语义:incoming 没传敏感键则保留 existing, 让前端"全对象 PUT"流程在脱敏后无感工作;显式提供新 token 仍会覆盖。 - 前端 EditAccountModal 修复脱敏后会崩的两处兜底:apikey 必填检查和 Vertex SA JSON 存在性校验改读 credentials_status.has_*。 - 导出端点 /admin/accounts/data 走独立的 DataAccount 结构,按设计保留 完整 credentials 作为管理员备份路径。 测试:RedactCredentials 单元测试、mapper 端到端 JSON 断言(确认序列化 后无 token 子串)、UpdateAccount 合并语义三种场景(保留 / 覆盖 / 空 map 跳过)。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
a466e80ed6
commit
0f8e2d0934
@@ -3343,13 +3343,12 @@ const handleSubmit = async () => {
|
||||
}
|
||||
|
||||
// Handle API key
|
||||
// 后端响应已脱敏:currentCredentials 不会再包含 api_key 原文。
|
||||
// 用户填入新值则覆盖;留空且后端已存在旧 key(看 credentials_status)则不带 api_key 字段,
|
||||
// 由后端合并保留;两者都无才报错。
|
||||
if (editApiKey.value.trim()) {
|
||||
// User provided a new API key
|
||||
newCredentials.api_key = editApiKey.value.trim()
|
||||
} else if (currentCredentials.api_key) {
|
||||
// Preserve existing api_key
|
||||
newCredentials.api_key = currentCredentials.api_key
|
||||
} else {
|
||||
} else if (!props.account.credentials_status?.has_api_key) {
|
||||
appStore.showError(t('admin.accounts.apiKeyIsRequired'))
|
||||
return
|
||||
}
|
||||
@@ -3434,7 +3433,11 @@ const handleSubmit = async () => {
|
||||
return
|
||||
}
|
||||
|
||||
if (!currentCredentials.service_account_json && !currentCredentials.service_account) {
|
||||
// SA JSON 已脱敏不再随 credentials 返回,存在性改读 credentials_status。
|
||||
const hasExistingServiceAccountJson =
|
||||
props.account.credentials_status?.has_service_account_json ||
|
||||
props.account.credentials_status?.has_service_account
|
||||
if (!hasExistingServiceAccountJson) {
|
||||
appStore.showError(t('admin.accounts.vertexSaJsonRequired'))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -795,7 +795,12 @@ export interface Account {
|
||||
notes?: string | null
|
||||
platform: AccountPlatform
|
||||
type: AccountType
|
||||
// 后端响应里 credentials 已脱敏:access_token / refresh_token / id_token /
|
||||
// api_key / session_key / cookie / aws_secret_access_key / aws_session_token /
|
||||
// service_account_json / service_account / private_key 不会出现,
|
||||
// 改为通过 credentials_status.has_<key> 暴露存在性。
|
||||
credentials?: Record<string, unknown>
|
||||
credentials_status?: Record<string, boolean>
|
||||
// Extra fields including Codex usage, OpenAI compact capability, and model-level rate limits.
|
||||
extra?: (CodexUsageSnapshot & OpenAICompactState & {
|
||||
model_rate_limits?: Record<string, { rate_limited_at: string; rate_limit_reset_at: string }>
|
||||
|
||||
Reference in New Issue
Block a user