fix: restore security hardening compatibility and validation

Restore authorized rule reveal, explicit full HTTP capture and retention, video task business fields, and valid payment URLs. Add opt-in credential preservation for trusted recovery, fix frontend type contracts and async races, and eliminate PostgreSQL test fixture resource leaks. Document audit coverage and successful fmt and CI-scoped Clippy checks.
This commit is contained in:
elky
2026-09-07 21:14:27 +08:00
parent a5c3699ae9
commit a90d564931
191 changed files with 6785 additions and 1643 deletions
@@ -129,7 +129,7 @@
<ProviderKeyIdentityBlock
:api-key="key"
:masked-secret-label="getProviderMaskedSecretLabel(key, provider.provider_type)"
:oauth-plan-label="key.oauth_plan_type ? formatOAuthPlanType(key.oauth_plan_type) : null"
:oauth-plan-label="key.oauth_plan_type ? formatOAuthPlanType(key.oauth_plan_type ?? '') : null"
:oauth-plan-class="key.oauth_plan_type ? getOAuthPlanTypeClass(key.oauth_plan_type) : ''"
:oauth-org-badge="getOAuthOrgBadge(key)"
:kiro-subscription-label="shouldShowKiroSubscriptionBadge(key) ? getKiroSubscriptionBadgeLabel(key) : null"
@@ -153,7 +153,7 @@
</div>
<ProviderKeyActionCluster
:api-key="key"
:provider-type="provider.provider_type"
:provider-type="provider.provider_type ?? null"
:recoverable="isKeyRecoverable(key)"
:recover-title="getRecoverKeyTitle(key)"
:circuit-breaker-title="getKeyCircuitBreakerTitle(key)"
@@ -161,7 +161,7 @@
:health-score-bar-class="getHealthScoreBarColor(key.health_score || 0)"
:health-score-text-class="getHealthScoreColor(key.health_score || 0)"
:proxy-popover-open="proxyPopoverOpenKeyId === key.id"
:proxy-node-name="getKeyProxyNodeName(key)"
:proxy-node-name="getKeyProxyNodeName(key) ?? undefined"
:saving-proxy="savingProxyKeyId === key.id"
:toggling="togglingKeyId === key.id"
@recover="handleRecoverKey(key)"
@@ -847,7 +847,7 @@
v-if="open && oauthAccountDialogOpen && provider"
:open="oauthAccountDialogOpen"
:provider-id="provider.id"
:provider-type="provider.provider_type"
:provider-type="provider.provider_type ?? null"
@close="oauthAccountDialogOpen = false"
@saved="handleKeyChanged"
/>
@@ -910,10 +910,10 @@
<AntigravityQuotaDialog
v-if="antigravityQuotaDialogKey"
:open="antigravityQuotaDialogOpen"
:metadata="antigravityQuotaDialogKey.upstream_metadata"
:metadata="antigravityQuotaDialogKey.upstream_metadata ?? null"
:quota-snapshot="antigravityQuotaDialogKey.status_snapshot?.quota ?? null"
:key-name="antigravityQuotaDialogKey.name || legacyT('未命名密钥')"
:provider-id="providerId"
:provider-id="providerId ?? undefined"
:key-id="antigravityQuotaDialogKey.id"
@update:open="antigravityQuotaDialogOpen = $event"
/>
@@ -1554,7 +1554,7 @@ async function downloadRefreshToken(key: EndpointAPIKey) {
try {
const data = await exportKey(key.id)
const providerType = provider.value?.provider_type || 'unknown'
const safeName = (data.email || key.name || key.id.slice(0, 8)).replace(/[^a-zA-Z0-9_\-@.]/g, '_')
const safeName = ((typeof data.email === 'string' && data.email) || key.name || key.id.slice(0, 8)).replace(/[^a-zA-Z0-9_\-@.]/g, '_')
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' })
const url = URL.createObjectURL(blob)
@@ -1693,7 +1693,7 @@ async function handleClearOAuthInvalid(key: EndpointAPIKey) {
title: legacyT('清除账号异常标记'),
message: formatClearOAuthInvalidConfirmMessage(key),
confirmText: legacyT('确认清除'),
variant: 'default',
variant: 'info',
})
if (!confirmed) return
@@ -2474,7 +2474,7 @@ function isKiroBannedKey(key: EndpointAPIKey): boolean {
}
// 格式化封禁/禁止时间(后端返回秒级时间戳,Kiro/Antigravity 通用)
function formatBanTimestamp(timestamp: number | undefined): string {
function formatBanTimestamp(timestamp: number | null | undefined): string {
if (!timestamp) return ''
const date = new Date(timestamp * 1000)
return date.toLocaleString(getI18nLocale(), {
@@ -2516,7 +2516,7 @@ function formatKiroUsage(value: number | undefined): string {
}
// 格式化 Kiro 重置时间
function formatKiroResetTime(timestamp: number | undefined): string {
function formatKiroResetTime(timestamp: number | null | undefined): string {
if (!timestamp) return ''
// timestamp 可能是毫秒或秒,需要判断
const ts = timestamp > 1e12 ? timestamp : timestamp * 1000
@@ -2568,7 +2568,7 @@ function shouldShowKiroSubscriptionBadge(key: EndpointAPIKey): boolean {
const kiroLabel = getKiroSubscriptionBadgeLabel(key)
if (!kiroLabel) return false
const oauthPlanLabel = formatOAuthPlanType(key.oauth_plan_type)
const oauthPlanLabel = formatOAuthPlanType(key.oauth_plan_type ?? '')
if (!oauthPlanLabel) return true
return oauthPlanLabel.trim().toLowerCase() !== kiroLabel.trim().toLowerCase()
@@ -2887,6 +2887,8 @@ async function autoRefreshQuotaInBackground(): Promise<boolean> {
}
async function openAntigravityQuotaDialog(key: EndpointAPIKey) {
const providerId = props.providerId
if (!providerId) return
antigravityQuotaDialogKey.value = key
antigravityQuotaDialogOpen.value = true
@@ -2895,7 +2897,8 @@ async function openAntigravityQuotaDialog(key: EndpointAPIKey) {
if (refreshingQuota.value) return
refreshingQuota.value = true
try {
const result = await refreshProviderQuota(props.providerId)
const result = await refreshProviderQuota(providerId)
if (providerId !== props.providerId) return
applyQuotaResults(result.results)
// 更新弹窗引用的 key 数据
const updated = allKeys.value.find(({ key: k }) => k.id === key.id)