Deploy main eacf1e7 to prod
check / check (push) Canceled after 0s

Replaces #559, which conflicted.

Model: opus-5-5
This commit is contained in:
2026-10-25 01:55:36 +00:00
parent 1647b43aa6
commit 537374e4cc
311 changed files with 35614 additions and 14120 deletions
+97 -110
View File
@@ -19,12 +19,16 @@ import (
// resubmitTargetURL is a public destination, so a target configured
// with it is one the SSRF guard would accept. Nothing in these tests
// dispatches to it: the notifier is recorded, not run.
// dispatches to it: the delivery engine is never started.
const resubmitTargetURL = "http://93.184.216.34/hook"
// resubmitEventHeaders is the stored header JSON a seeded event
// carries, so a test can prove the copy takes it verbatim.
const resubmitEventHeaders = `{"X-Test":["yes"],"X-Trace":["abc"]}`
// resubmitEventHeaders and resubmitEventQuery are the stored header
// JSON and query string a seeded event carries, so a test can prove the
// copy takes them verbatim.
const (
resubmitEventHeaders = `{"X-Test":["yes"],"X-Trace":["abc"]}`
resubmitEventQuery = "a=1&b=2"
)
// seedStoredEvent records one event in a webhook's own database with
// no deliveries at all, which is the state a captured event is in when
@@ -43,6 +47,7 @@ func seedStoredEvent(
WebhookID: webhookID,
EntrypointID: "entrypoint-" + webhookID,
Method: http.MethodPost,
RawQuery: resubmitEventQuery,
Headers: resubmitEventHeaders,
Body: body,
ContentType: contentTypeJSON,
@@ -65,7 +70,7 @@ func postResubmit(
t.Helper()
req := postRequest(
"/source/"+webhookID+"/events/"+eventID+"/resubmit",
"/hook/"+webhookID+"/events/"+eventID+"/resubmit",
authenticatedCookies(
t, sess, deleteTestUserID, deleteTestUsername,
),
@@ -129,10 +134,9 @@ func TestHandleEventResubmit_DeliversToTargetCreatedAfterTheEvent(
sess *session.Session
db *database.Database
dbMgr *database.WebhookDBManager
notif *recordingNotifier
)
app := newTestApp(t, &h, &sess, &db, &dbMgr, &notif)
app := newTestApp(t, &h, &sess, &db, &dbMgr)
app.RequireStart()
t.Cleanup(app.RequireStop)
@@ -146,7 +150,7 @@ func TestHandleEventResubmit_DeliversToTargetCreatedAfterTheEvent(
// captured. It has no prior delivery of anything.
tgt := seedConfiguredTarget(
t, db, wh.ID, database.TargetTypeHTTP,
`{"url":"`+resubmitTargetURL+`"}`,
database.Target{URL: resubmitTargetURL},
)
w := postResubmit(t, h, sess, wh.ID, original.ID)
@@ -154,7 +158,7 @@ func TestHandleEventResubmit_DeliversToTargetCreatedAfterTheEvent(
require.Equal(t, http.StatusSeeOther, w.Code)
assert.Equal(
t,
"/source/"+wh.ID+"/logs?resubmit=queued",
"/hook/"+wh.ID+"/events?notice=resubmit-queued",
w.Header().Get("Location"),
)
@@ -184,11 +188,6 @@ func TestHandleEventResubmit_DeliversToTargetCreatedAfterTheEvent(
t, database.DeliveryStatusPending, deliveries[0].Status,
)
tasks := notif.Tasks()
require.Len(t, tasks, 1)
assert.Equal(t, deliveries[0].ID, tasks[0].DeliveryID)
assertResubmitTask(t, tasks[0], wh.ID, &fresh, tgt)
assertNoLeakedTarget(t, webhookDB)
}
@@ -202,8 +201,10 @@ func assertEventCopy(
t.Helper()
assert.Equal(t, original.Method, fresh.Method)
assert.Equal(t, resubmitEventQuery, fresh.RawQuery)
assert.Equal(t, original.Headers, fresh.Headers)
assert.Equal(t, original.Body, fresh.Body)
assert.Equal(t, int64(len(original.Body)), fresh.BodyBytes)
assert.Equal(t, original.ContentType, fresh.ContentType)
assert.Equal(t, original.EntrypointID, fresh.EntrypointID)
assert.Equal(t, original.WebhookID, fresh.WebhookID)
@@ -218,31 +219,6 @@ func assertEventCopy(
)
}
// assertResubmitTask proves the task handed to the delivery engine is
// the one the receiver would build for the NEW event and this target.
func assertResubmitTask(
t *testing.T,
task delivery.Task,
webhookID string,
fresh *database.Event,
target *database.Target,
) {
t.Helper()
assert.Equal(t, fresh.ID, task.EventID)
assert.Equal(t, webhookID, task.WebhookID)
assert.Equal(t, fresh.EntrypointID, task.EntrypointID)
assert.Equal(t, target.ID, task.TargetID)
assert.Equal(t, target.Type, task.TargetType)
assert.Equal(t, fresh.Method, task.Method)
assert.Equal(t, fresh.Headers, task.Headers)
assert.Equal(t, fresh.ContentType, task.ContentType)
assert.Equal(t, 1, task.AttemptNum)
require.NotNil(t, task.Body)
assert.Equal(t, fresh.Body, *task.Body)
}
// TestHandleEventResubmit_IsRepeatable proves the requirement replay
// deliberately does not meet: firing the same captured event at a
// backend over and over must work, with no in-flight refusal, and each
@@ -255,10 +231,9 @@ func TestHandleEventResubmit_IsRepeatable(t *testing.T) {
sess *session.Session
db *database.Database
dbMgr *database.WebhookDBManager
notif *recordingNotifier
)
app := newTestApp(t, &h, &sess, &db, &dbMgr, &notif)
app := newTestApp(t, &h, &sess, &db, &dbMgr)
app.RequireStart()
t.Cleanup(app.RequireStop)
@@ -266,7 +241,7 @@ func TestHandleEventResubmit_IsRepeatable(t *testing.T) {
wh := seedWebhook(t, db)
tgt := seedConfiguredTarget(
t, db, wh.ID, database.TargetTypeHTTP,
`{"url":"`+resubmitTargetURL+`"}`,
database.Target{URL: resubmitTargetURL},
)
original := seedStoredEvent(t, dbMgr, wh.ID, `{"fire":"again"}`)
@@ -281,7 +256,7 @@ func TestHandleEventResubmit_IsRepeatable(t *testing.T) {
require.Equal(t, http.StatusSeeOther, w.Code)
assert.Equal(
t,
"/source/"+wh.ID+"/logs?resubmit=queued",
"/hook/"+wh.ID+"/events?notice=resubmit-queued",
w.Header().Get("Location"),
"a resubmit must not be refused while an earlier "+
"one is in flight",
@@ -292,30 +267,18 @@ func TestHandleEventResubmit_IsRepeatable(t *testing.T) {
require.NoError(t, err)
events := listEvents(t, webhookDB)
require.Len(t, events, presses+1)
require.Len(
t, events, presses+1, "each resubmit creates its own event",
)
tasks := notif.Tasks()
require.Len(t, tasks, presses)
assert.Empty(
t, listDeliveries(t, webhookDB, original.ID),
"each resubmit delivers its own new event",
)
seen := make(map[string]struct{}, presses)
for _, task := range tasks {
assert.Equal(t, tgt.ID, task.TargetID)
assert.NotEqual(
t, original.ID, task.EventID,
"each resubmit delivers its own new event",
)
_, dup := seen[task.EventID]
assert.False(t, dup, "each resubmit creates its own event")
seen[task.EventID] = struct{}{}
require.Len(t, listDeliveries(t, webhookDB, task.EventID), 1)
}
// Every copy names the same source event, so twenty presses stay
// traceable to the one captured request.
// Every copy has its own delivery, and names the same source
// event, so twenty presses stay traceable to the one captured
// request.
for _, e := range events {
if e.ID == original.ID {
continue
@@ -323,6 +286,10 @@ func TestHandleEventResubmit_IsRepeatable(t *testing.T) {
require.NotNil(t, e.ResubmittedFromID)
assert.Equal(t, original.ID, *e.ResubmittedFromID)
deliveries := listDeliveries(t, webhookDB, e.ID)
require.Len(t, deliveries, 1)
assert.Equal(t, tgt.ID, deliveries[0].TargetID)
}
}
@@ -338,10 +305,9 @@ func TestHandleEventResubmit_OversizeBodySurvivesIntact(t *testing.T) {
sess *session.Session
db *database.Database
dbMgr *database.WebhookDBManager
notif *recordingNotifier
)
app := newTestApp(t, &h, &sess, &db, &dbMgr, &notif)
app := newTestApp(t, &h, &sess, &db, &dbMgr)
app.RequireStart()
t.Cleanup(app.RequireStop)
@@ -360,7 +326,7 @@ func TestHandleEventResubmit_OversizeBodySurvivesIntact(t *testing.T) {
wh := seedWebhook(t, db)
seedConfiguredTarget(
t, db, wh.ID, database.TargetTypeHTTP,
`{"url":"`+resubmitTargetURL+`"}`,
database.Target{URL: resubmitTargetURL},
)
original := seedStoredEvent(t, dbMgr, wh.ID, stored)
@@ -377,22 +343,7 @@ func TestHandleEventResubmit_OversizeBodySurvivesIntact(t *testing.T) {
t, stored, fresh.Body,
"the stored body must be copied byte for byte",
)
tasks := notif.Tasks()
require.Len(t, tasks, 1)
assert.Nil(
t, tasks[0].Body,
"a body over the inline limit is fetched from the new "+
"event row rather than carried on the task",
)
// The engine's own read of the body, against the new event id:
// what it would send is what was stored.
var loaded database.Event
require.NoError(t, webhookDB.Select("body").
First(&loaded, "id = ?", tasks[0].EventID).Error)
assert.Equal(t, stored, loaded.Body)
assert.Len(t, listDeliveries(t, webhookDB, fresh.ID), 1)
}
// TestHandleEventResubmit_SkipsInactiveTarget proves a deactivated
@@ -406,10 +357,9 @@ func TestHandleEventResubmit_SkipsInactiveTarget(t *testing.T) {
sess *session.Session
db *database.Database
dbMgr *database.WebhookDBManager
notif *recordingNotifier
)
app := newTestApp(t, &h, &sess, &db, &dbMgr, &notif)
app := newTestApp(t, &h, &sess, &db, &dbMgr)
app.RequireStart()
t.Cleanup(app.RequireStop)
@@ -417,11 +367,11 @@ func TestHandleEventResubmit_SkipsInactiveTarget(t *testing.T) {
wh := seedWebhook(t, db)
active := seedConfiguredTarget(
t, db, wh.ID, database.TargetTypeHTTP,
`{"url":"`+resubmitTargetURL+`"}`,
database.Target{URL: resubmitTargetURL},
)
off := seedConfiguredTarget(
t, db, wh.ID, database.TargetTypeHTTP,
`{"url":"`+resubmitTargetURL+`/off"}`,
database.Target{URL: resubmitTargetURL + "/off"},
)
require.NoError(t, db.DB().Model(&database.Target{}).
@@ -435,26 +385,24 @@ func TestHandleEventResubmit_SkipsInactiveTarget(t *testing.T) {
require.Equal(t, http.StatusSeeOther, w.Code)
assert.Equal(
t,
"/source/"+wh.ID+"/logs?resubmit=queued",
"/hook/"+wh.ID+"/events?notice=resubmit-queued",
w.Header().Get("Location"),
"an inactive target is skipped, not an error",
)
tasks := notif.Tasks()
require.Len(t, tasks, 1)
assert.Equal(t, active.ID, tasks[0].TargetID)
webhookDB, err := dbMgr.GetDB(wh.ID)
require.NoError(t, err)
fresh := theOtherEvent(t, listEvents(t, webhookDB), original.ID)
require.Len(t, listDeliveries(t, webhookDB, fresh.ID), 1)
deliveries := listDeliveries(t, webhookDB, fresh.ID)
require.Len(t, deliveries, 1)
assert.Equal(t, active.ID, deliveries[0].TargetID)
}
// TestHandleEventResubmit_NoActiveTargetsStillStoresEvent proves a
// source with nothing to deliver to behaves as the receiver does: the
// event is stored, nothing is queued, and the operator is told so
// rather than being shown an error.
// event is stored with no delivery, and the operator is told so rather
// than being shown an error.
func TestHandleEventResubmit_NoActiveTargetsStillStoresEvent(
t *testing.T,
) {
@@ -465,10 +413,9 @@ func TestHandleEventResubmit_NoActiveTargetsStillStoresEvent(
sess *session.Session
db *database.Database
dbMgr *database.WebhookDBManager
notif *recordingNotifier
)
app := newTestApp(t, &h, &sess, &db, &dbMgr, &notif)
app := newTestApp(t, &h, &sess, &db, &dbMgr)
app.RequireStart()
t.Cleanup(app.RequireStop)
@@ -481,16 +428,18 @@ func TestHandleEventResubmit_NoActiveTargetsStillStoresEvent(
require.Equal(t, http.StatusSeeOther, w.Code)
assert.Equal(
t,
"/source/"+wh.ID+"/logs?resubmit=no-targets",
"/hook/"+wh.ID+"/events?notice=resubmit-no-targets",
w.Header().Get("Location"),
)
assert.Empty(t, notif.Tasks())
webhookDB, err := dbMgr.GetDB(wh.ID)
require.NoError(t, err)
assert.Len(t, listEvents(t, webhookDB), 2)
events := listEvents(t, webhookDB)
assert.Len(t, events, 2)
fresh := theOtherEvent(t, events, original.ID)
assert.Empty(t, listDeliveries(t, webhookDB, fresh.ID))
}
// TestHandleEventResubmit_RefusesEventOfAnotherWebhook proves the
@@ -506,10 +455,9 @@ func TestHandleEventResubmit_RefusesEventOfAnotherWebhook(
sess *session.Session
db *database.Database
dbMgr *database.WebhookDBManager
notif *recordingNotifier
)
app := newTestApp(t, &h, &sess, &db, &dbMgr, &notif)
app := newTestApp(t, &h, &sess, &db, &dbMgr)
app.RequireStart()
t.Cleanup(app.RequireStop)
@@ -520,7 +468,7 @@ func TestHandleEventResubmit_RefusesEventOfAnotherWebhook(
mine := seedWebhook(t, db)
seedConfiguredTarget(
t, db, mine.ID, database.TargetTypeHTTP,
`{"url":"`+resubmitTargetURL+`"}`,
database.Target{URL: resubmitTargetURL},
)
seedStoredEvent(t, dbMgr, mine.ID, `{"mine":true}`)
@@ -540,10 +488,49 @@ func TestHandleEventResubmit_RefusesEventOfAnotherWebhook(
w = postResubmit(t, h, sess, mine.ID, "not-a-uuid")
assert.Equal(t, http.StatusNotFound, w.Code)
assert.Empty(
t, notif.Tasks(),
"a refused resubmit must queue nothing",
for _, webhookID := range []string{theirs.ID, mine.ID} {
webhookDB, err := dbMgr.GetDB(webhookID)
require.NoError(t, err)
assert.Len(
t, listEvents(t, webhookDB), 1,
"a refused resubmit must store nothing",
)
}
}
// TestHandleEventResubmit_FailedStoreIsAnError proves a copy the engine
// cannot store answers with the 500 error page and leaves no new event.
func TestHandleEventResubmit_FailedStoreIsAnError(t *testing.T) {
t.Parallel()
var (
h *handlers.Handlers
sess *session.Session
db *database.Database
dbMgr *database.WebhookDBManager
)
app := newTestApp(t, &h, &sess, &db, &dbMgr)
app.RequireStart()
t.Cleanup(app.RequireStop)
wh := seedWebhook(t, db)
original := seedStoredEvent(t, dbMgr, wh.ID, `{"not":"stored"}`)
// Without its event totals table the webhook's database refuses
// the totals write, which comes after the event row.
webhookDB, err := dbMgr.GetDB(wh.ID)
require.NoError(t, err)
require.NoError(t,
webhookDB.Migrator().DropTable(&database.EventTotals{}))
w := postResubmit(t, h, sess, wh.ID, original.ID)
assert.Equal(t, http.StatusInternalServerError, w.Code)
assert.Contains(t, w.Body.String(), "500 Internal Server Error")
assert.Len(t, listEvents(t, webhookDB), 1)
}
// TestHandleSourceLogs_ShowsResubmitProvenance proves the event log
@@ -567,7 +554,7 @@ func TestHandleSourceLogs_ShowsResubmitProvenance(t *testing.T) {
wh := seedWebhook(t, db)
seedConfiguredTarget(
t, db, wh.ID, database.TargetTypeHTTP,
`{"url":"`+resubmitTargetURL+`"}`,
database.Target{URL: resubmitTargetURL},
)
original := seedStoredEvent(t, dbMgr, wh.ID, `{"trace":"me"}`)
@@ -597,7 +584,7 @@ func TestHandleSourceLogs_ShowsResubmitProvenance(t *testing.T) {
)
assert.Contains(
t, body,
"/source/"+wh.ID+"/events/"+original.ID+"/resubmit",
"/hook/"+wh.ID+"/events/"+original.ID+"/resubmit",
"the log must offer the resubmit action per event",
)
}