完善证书接口错误信息脱敏

This commit is contained in:
xuans 2026-08-31 01:50:59 +08:00
parent 6d04ba8c89
commit 43c51621ad
5 changed files with 69 additions and 5 deletions

View File

@ -114,7 +114,10 @@ func (s *Server) deployCertificateCurrent(c *gin.Context) {
result, report, err := s.deployCertificate(c.Request.Context(), strings.TrimSpace(c.Param("id")))
response := gin.H{"certificate": result, "deployment": report}
if err != nil {
response["error"] = err.Error()
if s.logger != nil {
s.logger.Error("证书部署失败", "error", err)
}
response["error"] = "证书部署失败"
c.JSON(http.StatusBadGateway, response)
return
}
@ -136,7 +139,10 @@ func (s *Server) importCertificateFiles(c *gin.Context) {
result, report, err := s.importCertificate(c.Request.Context(), request)
response := gin.H{"certificate": result, "deployment": report}
if err != nil {
response["error"] = err.Error()
if s.logger != nil {
s.logger.Error("证书导入失败", "error", err)
}
response["error"] = "证书导入失败"
c.JSON(http.StatusBadGateway, response)
return
}

View File

@ -99,6 +99,52 @@ func TestCertificateListHidesReaderErrors(t *testing.T) {
t.Fatalf("certificate list did not include the safe error message: %s", body)
}
}
func TestCertificateDeployAPIHidesWorkflowErrors(t *testing.T) {
server := New(Dependencies{
Config: readyTestConfig(t.TempDir()),
Repository: &stubDomainRepository{},
DeployCertificate: func(context.Context, string) (certificate.IssueResult, deployment.Report, error) {
return certificate.IssueResult{}, deployment.Report{}, errors.New("private-key-secret-path")
},
})
response := httptest.NewRecorder()
serveAdminHTTP(server, response, httptest.NewRequest(http.MethodPost, "/api/v1/domains/domain-1/deploy", nil))
if response.Code != http.StatusBadGateway {
t.Fatalf("certificate deployment failure returned an unexpected status: %d %s", response.Code, response.Body.String())
}
body := response.Body.String()
if strings.Contains(body, "private-key-secret-path") {
t.Fatalf("certificate deployment leaked an internal error: %s", body)
}
if !strings.Contains(body, "\u8bc1\u4e66\u90e8\u7f72\u5931\u8d25") {
t.Fatalf("certificate deployment did not include the safe error message: %s", body)
}
}
func TestCertificateImportAPIHidesWorkflowErrors(t *testing.T) {
server := New(Dependencies{
Config: readyTestConfig(t.TempDir()),
Repository: &stubDomainRepository{},
ImportCertificate: func(context.Context, certificate.ImportRequest) (certificate.IssueResult, deployment.Report, error) {
return certificate.IssueResult{}, deployment.Report{}, errors.New("private-key-secret-path")
},
})
body := `{"certificate_path":"/ssl/cert.pem","private_key_path":"/ssl/key.pem"}`
request := httptest.NewRequest(http.MethodPost, "/api/v1/domains/domain-1/certificate/import", strings.NewReader(body))
request.Header.Set("Content-Type", "application/json")
response := httptest.NewRecorder()
serveAdminHTTP(server, response, request)
if response.Code != http.StatusBadGateway {
t.Fatalf("certificate import failure returned an unexpected status: %d %s", response.Code, response.Body.String())
}
responseBody := response.Body.String()
if strings.Contains(responseBody, "private-key-secret-path") {
t.Fatalf("certificate import leaked an internal error: %s", responseBody)
}
if !strings.Contains(responseBody, "\u8bc1\u4e66\u5bfc\u5165\u5931\u8d25") {
t.Fatalf("certificate import did not include the safe error message: %s", responseBody)
}
}
func TestCertificateImportAPIAcceptsMultipartFiles(t *testing.T) {
var received certificate.ImportRequest
importer := func(_ context.Context, request certificate.ImportRequest) (certificate.IssueResult, deployment.Report, error) {

View File

@ -18,10 +18,13 @@ func (s *Server) restoreCertificateVersion(c *gin.Context) {
version := strings.TrimSpace(c.Param("version"))
result, err := s.restoreVersion(c.Request.Context(), strings.TrimSpace(c.Param("id")), version)
if err != nil {
if s.logger != nil {
s.logger.Error("恢复证书历史版本失败", "domain_id", c.Param("id"), "version", version, "error", err)
}
if s.audit != nil {
_ = s.audit.CreateAuditLog(c.Request.Context(), repository.AuditLog{
Action: "certificate_version_restore", ResourceType: "certificate", ResourceID: c.Param("id"), Status: "failed",
Message: "恢复证书历史版本失败:" + err.Error(),
Message: "恢复证书历史版本失败",
})
}
s.writeError(c, certificateErrorStatus(err), "恢复证书历史版本失败", err)

View File

@ -82,7 +82,7 @@ func TestRestoreCertificateVersionAPIRecordsSuccessAudit(t *testing.T) {
func TestRestoreCertificateVersionAPIRecordsFailureAudit(t *testing.T) {
server, audit := newRestoreAPITestServer(t, func(context.Context, string, string) (certificate.IssueResult, error) {
return certificate.IssueResult{}, errors.New("simulated restore failure")
return certificate.IssueResult{}, errors.New("private-key-secret-path")
})
request := httptest.NewRequest(http.MethodPost, "/api/v1/domains/domain-1/certificate/versions/bad/restore", nil)
request.AddCookie(&http.Cookie{Name: authCookieName, Value: "test-session"})
@ -91,6 +91,12 @@ func TestRestoreCertificateVersionAPIRecordsFailureAudit(t *testing.T) {
if response.Code != http.StatusUnprocessableEntity {
t.Fatalf("unexpected failure response: %d %s", response.Code, response.Body.String())
}
if strings.Contains(response.Body.String(), "private-key-secret-path") {
t.Fatalf("certificate restore leaked an internal error: %s", response.Body.String())
}
if strings.Contains(audit.items[0].Message, "private-key-secret-path") {
t.Fatalf("certificate restore audit leaked an internal error: %+v", audit.items[0])
}
if len(audit.items) != 1 || audit.items[0].Status != "failed" || audit.items[0].Action != "certificate_version_restore" {
t.Fatalf("failure audit was not recorded: %+v", audit.items)
}

View File

@ -15,7 +15,10 @@ func (s *Server) runRenewalNow(c *gin.Context) {
report, err := s.runRenewal(c.Request.Context())
response := gin.H{"report": report}
if err != nil {
response["error"] = err.Error()
if s.logger != nil {
s.logger.Error("执行证书续期失败", "error", err)
}
response["error"] = "执行证书续期失败"
c.JSON(http.StatusConflict, response)
return
}