(t *testing.T)
| 200 | } |
| 201 | |
| 202 | func TestReplayProtection(t *testing.T) { |
| 203 | pool := testutil.TestDB(t) |
| 204 | receiver := testutil.SubscriberReceiver(t) |
| 205 | ts := testutil.TestServer(t, pool) |
| 206 | |
| 207 | user, _, _ := setupDomainAndAgent(t, ts, "agent@replay.example.com", "replay.example.com", "", "") |
| 208 | registerWebhook(t, ts, user.ID, receiver.Server.URL+"/received", |
| 209 | []string{"email.received"}, identity.WebhookFilters{}) |
| 210 | |
| 211 | msg := "From: alice@test.com\r\nTo: agent@replay.example.com\r\nSubject: Replay\r\n\r\nTest" |
| 212 | smtp.SendMail(ts.SMTPAddr, nil, "alice@test.com", []string{"agent@replay.example.com"}, []byte(msg)) |
| 213 | |
| 214 | tick(t, ts) |
| 215 | got := receiver.WaitFor(t, 5*time.Second, func(c []testutil.SubscriberCaptured) bool { return len(c) >= 1 }) |
| 216 | if len(got) != 1 { |
| 217 | t.Fatalf("got %d captures, want 1", len(got)) |
| 218 | } |
| 219 | data, _ := got[0].Envelope["data"].(map[string]any) |
| 220 | authHeaders := headers.AuthHeaders(receivedAuthHeaders(data)) |
| 221 | secrets := authHeaderSecrets(t, ts, user.ID) |
| 222 | |
| 223 | // Should verify with normal window |
| 224 | if !headers.Verify(secrets, authHeaders) { |
| 225 | t.Error("expected auth headers to verify within normal window") |
| 226 | } |
| 227 | |
| 228 | // Should reject with very tight window |
| 229 | time.Sleep(5 * time.Millisecond) |
| 230 | if headers.VerifyWithMaxAge(secrets, authHeaders, 1*time.Millisecond) { |
| 231 | t.Error("expected replay protection to reject stale headers") |
| 232 | } |
| 233 | } |
| 234 | |
| 235 | func TestOutboundResponseFormat(t *testing.T) { |
| 236 | t.Skip("requires an outbound SMTP relay configured in testutil.TestServer — tracked as test-infra work") |
nothing calls this directly
no test coverage detected