fix(security): 告警渠道 webhook 与 Bark Device Key 响应脱敏

AlertNotifyChannel.Config 原先以真值返回。钉钉/飞书/企微的 webhook URL 与 Bark
的 device key 本身就是凭据,泄露即可直接向对应群或设备推送消息,而列表接口对
具备 recharge:manage 的商户成员可见。

- 新增 service.MaskAlertChannel(s):在 HTTP 响应层脱敏,只保留密钥末 4 位。
  webhook 保留域名与路径以便辨认是哪一台机器人(钉钉/企微的 token 在查询串,
  飞书的 uuid 在路径末段),Server 属主机地址不遮蔽
- UpdateAlertChannel 识别回传的脱敏占位值并沿用库中真实密钥,使「只改名称/开关」
  不会被掩码覆盖;填写新值则正常替换
- 脱敏刻意不放在 service 层:CheckLowBalanceAndNotify 复用 ListAlertChannels
  的结果真正去发消息,在那一层脱敏会直接打断告警发送
- 前端表单补充说明文案

验证:新增 6 组测试,覆盖脱敏规则、三种机器人 URL 形态、脱敏结果仍能通过校验、
掩码回传不覆盖真实密钥,以及「service 层必须返回真值」这条关键边界。
This commit is contained in:
yml2213
2026-10-06 19:49:11 +08:00
parent 14d297a591
commit 7f984b0f4e
4 changed files with 342 additions and 7 deletions
+3 -3
View File
@@ -147,7 +147,7 @@ func (h *RechargeHandler) ListAlertChannels(c *gin.Context) {
response.ServerError(c, err.Error())
return
}
response.OK(c, list)
response.OK(c, service.MaskAlertChannels(list))
}
type alertChannelReq struct {
@@ -188,7 +188,7 @@ func (h *RechargeHandler) CreateAlertChannel(c *gin.Context) {
response.BadRequest(c, err.Error())
return
}
response.OK(c, channel)
response.OK(c, service.MaskAlertChannel(*channel))
}
// UpdateAlertChannel PUT /api/merchant/alert-channels/:id
@@ -208,7 +208,7 @@ func (h *RechargeHandler) UpdateAlertChannel(c *gin.Context) {
response.BadRequest(c, err.Error())
return
}
response.OK(c, channel)
response.OK(c, service.MaskAlertChannel(*channel))
}
// DeleteAlertChannel DELETE /api/merchant/alert-channels/:id