Address review findings F1-F4 for config and /health
Independent review Not signed off — 0 high, 3 medium (#2)
Independent review Not signed off — 0 high, 3 medium (#2)
- F1: read the secret from MEALQUEST_SECRET_KEY_FILE, the documented name. - F2: reject public URLs carrying userinfo, never echo the raw URL in config errors, and log only the public origin (scheme + host). - F3: validate the MEALQUEST_HTTP_ADDR port (numeric, 0-65535) during Load. - F4: return 405 with the API error JSON shape for unsupported methods. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,9 +14,37 @@ import (
|
||||
func NewRouter(logger *slog.Logger, version string) http.Handler {
|
||||
r := chi.NewRouter()
|
||||
r.Get("/health", health(logger, version))
|
||||
r.MethodNotAllowed(methodNotAllowed(logger))
|
||||
return r
|
||||
}
|
||||
|
||||
type errorBody struct {
|
||||
Error apiError `json:"error"`
|
||||
}
|
||||
|
||||
type apiError struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Retryable bool `json:"retryable"`
|
||||
}
|
||||
|
||||
func methodNotAllowed(logger *slog.Logger) http.HandlerFunc {
|
||||
body, err := json.Marshal(errorBody{Error: apiError{
|
||||
Code: "method_not_allowed",
|
||||
Message: "method not allowed",
|
||||
}})
|
||||
if err != nil {
|
||||
panic(err) // fixed string and bool fields cannot fail to marshal
|
||||
}
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
if _, err := w.Write(body); err != nil {
|
||||
logger.WarnContext(r.Context(), "write method not allowed response", "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type healthResponse struct {
|
||||
Status string `json:"status"`
|
||||
Version string `json:"version"`
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
"maps"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
@@ -48,4 +49,21 @@ func TestHealthRejectsOtherMethods(t *testing.T) {
|
||||
if rec.Code != http.StatusMethodNotAllowed {
|
||||
t.Errorf("status = %d, want 405", rec.Code)
|
||||
}
|
||||
if ct := rec.Header().Get("Content-Type"); ct != "application/json" {
|
||||
t.Errorf("Content-Type = %q, want application/json", ct)
|
||||
}
|
||||
var got struct {
|
||||
Error map[string]any `json:"error"`
|
||||
}
|
||||
if err := json.NewDecoder(rec.Body).Decode(&got); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := map[string]any{
|
||||
"code": "method_not_allowed",
|
||||
"message": "method not allowed",
|
||||
"retryable": false,
|
||||
}
|
||||
if !maps.Equal(got.Error, want) {
|
||||
t.Errorf("error body = %v, want %v", got.Error, want)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user