feat(calendar): store only token hash in database to enhance security
All checks were successful
Build & Push Docker Image / build (push) Successful in 6m25s
All checks were successful
Build & Push Docker Image / build (push) Successful in 6m25s
feat(federation): escape LIKE special characters in originDomain to prevent wildcard injection feat(oauth): redirect with token in hash fragment to avoid exposure in logs feat(OAuthCallback): retrieve token from hash fragment for improved security
This commit is contained in:
@@ -577,6 +577,9 @@ router.post('/calendar-event-deleted', federationReceiveLimiter, async (req, res
|
||||
|
||||
const db = getDb();
|
||||
|
||||
// Escape LIKE special characters in originDomain to prevent wildcard injection.
|
||||
const safeDomain = originDomain.replace(/[\\%_]/g, '\\$&');
|
||||
|
||||
// Collect all affected users before deleting (for email notifications)
|
||||
let affectedUsers = [];
|
||||
try {
|
||||
@@ -585,16 +588,16 @@ router.post('/calendar-event-deleted', federationReceiveLimiter, async (req, res
|
||||
`SELECT u.email, u.name, u.language, ci.title, ci.from_user
|
||||
FROM calendar_invitations ci
|
||||
JOIN users u ON ci.to_user_id = u.id
|
||||
WHERE ci.event_uid = ? AND ci.from_user LIKE ?`,
|
||||
[event_uid, `%@${originDomain}`]
|
||||
WHERE ci.event_uid = ? AND ci.from_user LIKE ? ESCAPE '\\'`,
|
||||
[event_uid, `%@${safeDomain}`]
|
||||
);
|
||||
// Users who already accepted (event in their calendar)
|
||||
const calUsers = await db.all(
|
||||
`SELECT u.email, u.name, u.language, ce.title, ce.federated_from AS from_user
|
||||
FROM calendar_events ce
|
||||
JOIN users u ON ce.user_id = u.id
|
||||
WHERE ce.uid = ? AND ce.federated_from LIKE ?`,
|
||||
[event_uid, `%@${originDomain}`]
|
||||
WHERE ce.uid = ? AND ce.federated_from LIKE ? ESCAPE '\\'`,
|
||||
[event_uid, `%@${safeDomain}`]
|
||||
);
|
||||
// Merge, deduplicate by email
|
||||
const seen = new Set();
|
||||
@@ -609,15 +612,15 @@ router.post('/calendar-event-deleted', federationReceiveLimiter, async (req, res
|
||||
// Remove from calendar_invitations for all users on this instance
|
||||
await db.run(
|
||||
`DELETE FROM calendar_invitations
|
||||
WHERE event_uid = ? AND from_user LIKE ?`,
|
||||
[event_uid, `%@${originDomain}`]
|
||||
WHERE event_uid = ? AND from_user LIKE ? ESCAPE '\\'`,
|
||||
[event_uid, `%@${safeDomain}`]
|
||||
);
|
||||
|
||||
// Remove from calendar_events (accepted invitations) for all users on this instance
|
||||
await db.run(
|
||||
`DELETE FROM calendar_events
|
||||
WHERE uid = ? AND federated_from LIKE ?`,
|
||||
[event_uid, `%@${originDomain}`]
|
||||
WHERE uid = ? AND federated_from LIKE ? ESCAPE '\\'`,
|
||||
[event_uid, `%@${safeDomain}`]
|
||||
);
|
||||
|
||||
log.federation.info(`Calendar event ${event_uid} deleted (origin: ${originDomain})`);
|
||||
@@ -667,11 +670,13 @@ router.post('/room-deleted', federationReceiveLimiter, async (req, res) => {
|
||||
}
|
||||
|
||||
const db = getDb();
|
||||
// Escape LIKE special characters in originDomain to prevent wildcard injection.
|
||||
const safeDomain = originDomain.replace(/[\\%_]/g, '\\$&');
|
||||
// Mark all federated_rooms with this meet_id (room_uid) from this origin as deleted
|
||||
await db.run(
|
||||
`UPDATE federated_rooms SET deleted = 1, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE meet_id = ? AND from_user LIKE ?`,
|
||||
[room_uid, `%@${originDomain}`]
|
||||
WHERE meet_id = ? AND from_user LIKE ? ESCAPE '\\'`,
|
||||
[room_uid, `%@${safeDomain}`]
|
||||
);
|
||||
|
||||
log.federation.info(`Room ${room_uid} marked as deleted (origin: ${originDomain})`);
|
||||
|
||||
Reference in New Issue
Block a user