|
1 | 1 | package api |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "bytes" |
4 | 5 | "encoding/json" |
5 | 6 | "errors" |
6 | 7 | "fmt" |
@@ -157,17 +158,46 @@ func (a *ApplicationHandler) IngestEvent(w http.ResponseWriter, r *http.Request) |
157 | 158 |
|
158 | 159 | // 3.1 On Failure |
159 | 160 | // Return 400 Bad Request. |
160 | | - payload, err := extractPayloadFromIngestEventReq(r, maxIngestSize) |
| 161 | + // Read raw body for signature verification first (e.g., GitHub signs raw bytes) |
| 162 | + rawPayload, err := io.ReadAll(io.LimitReader(r.Body, int64(maxIngestSize))) |
161 | 163 | if err != nil { |
162 | | - a.A.Logger.WithError(err).Error("Failed to extract payload") |
| 164 | + a.A.Logger.WithError(err).Error("Failed to read request body") |
163 | 165 | _ = render.Render(w, r, util.NewErrorResponse("Invalid request format", http.StatusBadRequest)) |
164 | 166 | return |
165 | 167 | } |
166 | 168 |
|
167 | | - if err = v.VerifyRequest(r, payload); err != nil { |
168 | | - a.A.Logger.WithError(err).Error("Request verification failed") |
169 | | - _ = render.Render(w, r, util.NewErrorResponse("Request verification failed", http.StatusBadRequest)) |
170 | | - return |
| 169 | + // Restore body for subsequent reads |
| 170 | + r.Body = io.NopCloser(bytes.NewReader(rawPayload)) |
| 171 | + |
| 172 | + // Try raw-body verification first |
| 173 | + rawVerifyErr := v.VerifyRequest(r, rawPayload) |
| 174 | + |
| 175 | + var payload []byte |
| 176 | + if rawVerifyErr != nil { |
| 177 | + // Fallback: extract/convert payload (e.g., form -> JSON) and verify against that for backward compatibility |
| 178 | + payload, err = extractPayloadFromIngestEventReq(r, maxIngestSize) |
| 179 | + if err != nil { |
| 180 | + a.A.Logger.WithError(err).Error("Failed to extract payload") |
| 181 | + _ = render.Render(w, r, util.NewErrorResponse("Invalid request format", http.StatusBadRequest)) |
| 182 | + return |
| 183 | + } |
| 184 | + |
| 185 | + // Reset body before verification (some verifiers may inspect headers/body state) |
| 186 | + r.Body = io.NopCloser(bytes.NewReader(rawPayload)) |
| 187 | + |
| 188 | + if err = v.VerifyRequest(r, payload); err != nil { |
| 189 | + a.A.Logger.WithError(rawVerifyErr).Error("Request verification failed (raw and converted)") |
| 190 | + _ = render.Render(w, r, util.NewErrorResponse("Request verification failed", http.StatusBadRequest)) |
| 191 | + return |
| 192 | + } |
| 193 | + } else { |
| 194 | + // Raw verification succeeded; now convert for storage |
| 195 | + payload, err = extractPayloadFromIngestEventReq(r, maxIngestSize) |
| 196 | + if err != nil { |
| 197 | + a.A.Logger.WithError(err).Error("Failed to extract payload") |
| 198 | + _ = render.Render(w, r, util.NewErrorResponse("Invalid request format", http.StatusBadRequest)) |
| 199 | + return |
| 200 | + } |
171 | 201 | } |
172 | 202 |
|
173 | 203 | if len(payload) == 0 { |
|
0 commit comments