fix: 6 bugs (timeline path value, seer SSE cursor, erasure checksum, subtle compare, build status code, dead WorkerName branch)
Some checks failed
Test / test (push) Has been cancelled
Some checks failed
Test / test (push) Has been cancelled
This commit is contained in:
@@ -70,7 +70,7 @@ func (h *ForgeHandler) TriggerBuild(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
builds, err := h.Pipeline.BuildAll(req.Public)
|
||||
if err != nil {
|
||||
auth.JSON(w, http.StatusOK, map[string]any{
|
||||
auth.JSON(w, http.StatusInternalServerError, map[string]any{
|
||||
"ok": false,
|
||||
"message": err.Error(),
|
||||
})
|
||||
|
||||
@@ -36,10 +36,15 @@ func (h *SeerHandler) Stream(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
w.Header().Set("Connection", "keep-alive")
|
||||
|
||||
// Send last 20 events as backlog, track the most recent timestamp as cursor.
|
||||
var cursor string
|
||||
events, _ := h.Store.ListSeerEvents(20)
|
||||
for _, ev := range events {
|
||||
payload, _ := json.Marshal(ev)
|
||||
fmt.Fprintf(w, "data: %s\n\n", payload)
|
||||
if ev.CreatedAt > cursor {
|
||||
cursor = ev.CreatedAt
|
||||
}
|
||||
}
|
||||
flusher.Flush()
|
||||
|
||||
@@ -51,13 +56,18 @@ func (h *SeerHandler) Stream(w http.ResponseWriter, r *http.Request) {
|
||||
case <-r.Context().Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
events, err := h.Store.ListSeerEvents(5)
|
||||
newEvents, err := h.Store.ListSeerEventsSince(cursor, 50)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, ev := range events {
|
||||
for _, ev := range newEvents {
|
||||
payload, _ := json.Marshal(ev)
|
||||
fmt.Fprintf(w, "data: %s\n\n", payload)
|
||||
if ev.CreatedAt > cursor {
|
||||
cursor = ev.CreatedAt
|
||||
}
|
||||
}
|
||||
if len(newEvents) > 0 {
|
||||
flusher.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ func (h *SeerHub) fetchEvents(ctx context.Context, since string) ([]SeerEvent, e
|
||||
// TimelineHandler serves LOTL timeline merged with court events for a host.
|
||||
func TimelineHandler(c *Court) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
hostID := r.PathValue("host_id")
|
||||
hostID := r.PathValue("id")
|
||||
if hostID == "" {
|
||||
http.Error(w, "host_id required", http.StatusBadRequest)
|
||||
return
|
||||
|
||||
@@ -176,12 +176,12 @@ func (s *Service) Reconstruct(ctx context.Context, bundleID string, indices []in
|
||||
for i := 0; i < bundle.DataShards; i++ {
|
||||
out = append(out, shards[i]...)
|
||||
}
|
||||
// Trim padding — find actual length via checksum match
|
||||
sum := sha256.Sum256(out)
|
||||
trimmed := trimNullPadding(out)
|
||||
sum := sha256.Sum256(trimmed)
|
||||
if hex.EncodeToString(sum[:]) != bundle.Checksum {
|
||||
// Return best-effort; caller validates
|
||||
return nil, fmt.Errorf("reconstructed data checksum mismatch: bundle may be corrupt")
|
||||
}
|
||||
return trimNullPadding(out), nil
|
||||
return trimmed, nil
|
||||
}
|
||||
|
||||
func trimNullPadding(b []byte) []byte {
|
||||
|
||||
@@ -51,6 +51,47 @@ type SeerEvent struct {
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
|
||||
// ListSeerEventsSince returns events newer than the given SQLite timestamp string (exclusive).
|
||||
func (s *Store) ListSeerEventsSince(since string, limit int) ([]SeerEvent, error) {
|
||||
if limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
var rows interface {
|
||||
Close() error
|
||||
Next() bool
|
||||
Scan(dest ...any) error
|
||||
Err() error
|
||||
}
|
||||
var err error
|
||||
if since == "" {
|
||||
rows, err = s.db.Query(`
|
||||
SELECT id, host_id, event_type, payload_json, created_at
|
||||
FROM seer_events ORDER BY created_at ASC LIMIT ?`, limit)
|
||||
} else {
|
||||
rows, err = s.db.Query(`
|
||||
SELECT id, host_id, event_type, payload_json, created_at
|
||||
FROM seer_events WHERE created_at > ? ORDER BY created_at ASC LIMIT ?`, since, limit)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var events []SeerEvent
|
||||
for rows.Next() {
|
||||
var e SeerEvent
|
||||
var hostID sql.NullString
|
||||
if err := rows.Scan(&e.ID, &hostID, &e.EventType, &e.PayloadJSON, &e.CreatedAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if hostID.Valid {
|
||||
e.HostID = hostID.String
|
||||
}
|
||||
events = append(events, e)
|
||||
}
|
||||
return events, rows.Err()
|
||||
}
|
||||
|
||||
func nullString(s string) sql.NullString {
|
||||
if s == "" {
|
||||
return sql.NullString{}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package fleet
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
@@ -329,14 +330,7 @@ func (h *Hub) HandleBeacon(store *Store) http.HandlerFunc {
|
||||
}
|
||||
|
||||
func constantTimeEqual(a, b string) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
var v byte
|
||||
for i := 0; i < len(a); i++ {
|
||||
v |= a[i] ^ b[i]
|
||||
}
|
||||
return v == 0
|
||||
return subtle.ConstantTimeCompare([]byte(a), []byte(b)) == 1
|
||||
}
|
||||
|
||||
func coalesce(values ...string) string {
|
||||
|
||||
Reference in New Issue
Block a user