fix: Go 1.25 upgrade, codebase audit fixes, MCP server tests

Upgrade from Go 1.22 to 1.25 (minimum for MCP SDK, actively supported).
CI updated to match.

Codebase audit fixes:
- Local CA parseIP() now uses net.ParseIP — IP SANs no longer silently dropped
- Nil pointer guards in agent.go GetWorkWithTargets for target/cert enrichment
- MCP CreateCertificateInput marks owner_id/team_id as required
- NGINX connector uses CombinedOutput() — captures diagnostic output on failure
- Jobs handler validates JSON decode on rejection body — returns 400 on malformed
- CRL/OCSP handlers propagate requestID for error tracing

MCP server tests (26 tests):
- client_test.go: HTTP client coverage (GET/POST/PUT/DELETE, auth, 204, errors, binary)
- tools_test.go: tool registration, pagination, end-to-end flows with mock API

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Shankar
2026-03-23 17:36:25 -04:00
parent 7c8d4d29ff
commit d460950cce
10 changed files with 742 additions and 27 deletions
+14 -10
View File
@@ -450,14 +450,16 @@ func (h CertificateHandler) GetCRL(w http.ResponseWriter, r *http.Request) {
// GetDERCRL returns a DER-encoded X.509 CRL signed by the specified issuer.
// GET /api/v1/crl/{issuer_id}
func (h CertificateHandler) GetDERCRL(w http.ResponseWriter, r *http.Request) {
requestID, _ := r.Context().Value("request_id").(string)
if r.Method != http.MethodGet {
Error(w, http.StatusMethodNotAllowed, "Method not allowed")
ErrorWithRequestID(w, http.StatusMethodNotAllowed, "Method not allowed", requestID)
return
}
issuerID := strings.TrimPrefix(r.URL.Path, "/api/v1/crl/")
if issuerID == "" {
Error(w, http.StatusBadRequest, "Issuer ID is required")
ErrorWithRequestID(w, http.StatusBadRequest, "Issuer ID is required", requestID)
return
}
@@ -465,14 +467,14 @@ func (h CertificateHandler) GetDERCRL(w http.ResponseWriter, r *http.Request) {
if err != nil {
errMsg := err.Error()
if strings.Contains(errMsg, "not found") {
Error(w, http.StatusNotFound, errMsg)
ErrorWithRequestID(w, http.StatusNotFound, errMsg, requestID)
return
}
if strings.Contains(errMsg, "do not support") || strings.Contains(errMsg, "does not support") {
Error(w, http.StatusNotImplemented, errMsg)
ErrorWithRequestID(w, http.StatusNotImplemented, errMsg, requestID)
return
}
Error(w, http.StatusInternalServerError, "Failed to generate CRL")
ErrorWithRequestID(w, http.StatusInternalServerError, "Failed to generate CRL", requestID)
return
}
@@ -486,8 +488,10 @@ func (h CertificateHandler) GetDERCRL(w http.ResponseWriter, r *http.Request) {
// GET /api/v1/ocsp/{issuer_id}/{serial_hex}
// For simplicity, use GET with path params instead of binary POST.
func (h CertificateHandler) HandleOCSP(w http.ResponseWriter, r *http.Request) {
requestID, _ := r.Context().Value("request_id").(string)
if r.Method != http.MethodGet {
Error(w, http.StatusMethodNotAllowed, "Method not allowed")
ErrorWithRequestID(w, http.StatusMethodNotAllowed, "Method not allowed", requestID)
return
}
@@ -495,7 +499,7 @@ func (h CertificateHandler) HandleOCSP(w http.ResponseWriter, r *http.Request) {
path := strings.TrimPrefix(r.URL.Path, "/api/v1/ocsp/")
parts := strings.SplitN(path, "/", 2)
if len(parts) < 2 || parts[0] == "" || parts[1] == "" {
Error(w, http.StatusBadRequest, "Issuer ID and serial number are required")
ErrorWithRequestID(w, http.StatusBadRequest, "Issuer ID and serial number are required", requestID)
return
}
issuerID := parts[0]
@@ -505,14 +509,14 @@ func (h CertificateHandler) HandleOCSP(w http.ResponseWriter, r *http.Request) {
if err != nil {
errMsg := err.Error()
if strings.Contains(errMsg, "not found") {
Error(w, http.StatusNotFound, errMsg)
ErrorWithRequestID(w, http.StatusNotFound, errMsg, requestID)
return
}
if strings.Contains(errMsg, "do not support") || strings.Contains(errMsg, "does not support") {
Error(w, http.StatusNotImplemented, errMsg)
ErrorWithRequestID(w, http.StatusNotImplemented, errMsg, requestID)
return
}
Error(w, http.StatusInternalServerError, "Failed to generate OCSP response")
ErrorWithRequestID(w, http.StatusInternalServerError, "Failed to generate OCSP response", requestID)
return
}
+4 -1
View File
@@ -186,7 +186,10 @@ func (h JobHandler) RejectJob(w http.ResponseWriter, r *http.Request) {
Reason string `json:"reason"`
}
if r.Body != nil {
json.NewDecoder(r.Body).Decode(&body)
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
ErrorWithRequestID(w, http.StatusBadRequest, "Invalid request body", requestID)
return
}
}
if err := h.svc.RejectJob(jobID, body.Reason); err != nil {