fix(webhooks): skip replay protection for Test events (closes #71)
Build and Push Docker Image / build (push) Has been cancelled
CI / Security audit (push) Has been cancelled
CI / Tests & coverage (push) Has been cancelled
CI / Swagger Validation & Coverage (push) Has been cancelled
Licence Check / Licence compatibility and copyright header verification (push) Successful in 2m56s
Build and Push Docker Image / build (push) Has been cancelled
CI / Security audit (push) Has been cancelled
CI / Tests & coverage (push) Has been cancelled
CI / Swagger Validation & Coverage (push) Has been cancelled
Licence Check / Licence compatibility and copyright header verification (push) Successful in 2m56s
This commit is contained in:
@@ -476,7 +476,10 @@ router.post('/sonarr', webhookLimiter, (req, res) => {
|
|||||||
// Content-aware replay key components (Issue #62)
|
// Content-aware replay key components (Issue #62)
|
||||||
const contentId = req.body.downloadId || req.body.series?.id || null;
|
const contentId = req.body.downloadId || req.body.series?.id || null;
|
||||||
|
|
||||||
if (isReplay(eventType, resolvedInstanceName, eventDate, contentId)) {
|
// Skip replay protection for Test events
|
||||||
|
if (eventType === "Test") {
|
||||||
|
logToFile(`[Webhook] Sonarr Test event received — skipping replay protection`);
|
||||||
|
} else if (isReplay(eventType, resolvedInstanceName, eventDate, contentId)) {
|
||||||
logToFile(`[Webhook] Sonarr duplicate event ignored: ${eventType} @ ${eventDate} (contentId=${contentId || 'none'})`);
|
logToFile(`[Webhook] Sonarr duplicate event ignored: ${eventType} @ ${eventDate} (contentId=${contentId || 'none'})`);
|
||||||
return res.status(200).json({ received: true, duplicate: true });
|
return res.status(200).json({ received: true, duplicate: true });
|
||||||
}
|
}
|
||||||
@@ -637,7 +640,10 @@ router.post('/radarr', webhookLimiter, (req, res) => {
|
|||||||
// Content-aware replay key components (Issue #62)
|
// Content-aware replay key components (Issue #62)
|
||||||
const contentId = req.body.downloadId || req.body.movie?.id || null;
|
const contentId = req.body.downloadId || req.body.movie?.id || null;
|
||||||
|
|
||||||
if (isReplay(eventType, resolvedInstanceName, eventDate, contentId)) {
|
// Skip replay protection for Test events
|
||||||
|
if (eventType === "Test") {
|
||||||
|
logToFile(`[Webhook] Radarr Test event received — skipping replay protection`);
|
||||||
|
} else if (isReplay(eventType, resolvedInstanceName, eventDate, contentId)) {
|
||||||
logToFile(`[Webhook] Radarr duplicate event ignored: ${eventType} @ ${eventDate} (contentId=${contentId || 'none'})`);
|
logToFile(`[Webhook] Radarr duplicate event ignored: ${eventType} @ ${eventDate} (contentId=${contentId || 'none'})`);
|
||||||
return res.status(200).json({ received: true, duplicate: true });
|
return res.status(200).json({ received: true, duplicate: true });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -387,6 +387,38 @@ describe('Replay protection', () => {
|
|||||||
expect(first.body.duplicate).toBeUndefined();
|
expect(first.body.duplicate).toBeUndefined();
|
||||||
expect(second.body.duplicate).toBeUndefined();
|
expect(second.body.duplicate).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sonarr: Test events bypass replay protection and are not flagged as duplicates', async () => {
|
||||||
|
const app = makeApp();
|
||||||
|
const payload = {
|
||||||
|
eventType: 'Test',
|
||||||
|
instanceName: 'Main Sonarr',
|
||||||
|
date: '2026-05-19T13:00:00.000Z'
|
||||||
|
};
|
||||||
|
const first = await postSonarr(app, payload);
|
||||||
|
expect(first.status).toBe(200);
|
||||||
|
expect(first.body.duplicate).toBeUndefined();
|
||||||
|
|
||||||
|
const second = await postSonarr(app, payload);
|
||||||
|
expect(second.status).toBe(200);
|
||||||
|
expect(second.body.duplicate).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('radarr: Test events bypass replay protection and are not flagged as duplicates', async () => {
|
||||||
|
const app = makeApp();
|
||||||
|
const payload = {
|
||||||
|
eventType: 'Test',
|
||||||
|
instanceName: 'Main Radarr',
|
||||||
|
date: '2026-05-19T13:00:00.000Z'
|
||||||
|
};
|
||||||
|
const first = await postRadarr(app, payload);
|
||||||
|
expect(first.status).toBe(200);
|
||||||
|
expect(first.body.duplicate).toBeUndefined();
|
||||||
|
|
||||||
|
const second = await postRadarr(app, payload);
|
||||||
|
expect(second.status).toBe(200);
|
||||||
|
expect(second.body.duplicate).toBeUndefined();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user