mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-19 13:21:46 +00:00
fix(security): address notification system security audit findings
- SSRF: guard sendWebhook() with checkSsrf() + createPinnedAgent() to block
requests to loopback, link-local, private network, and cloud metadata endpoints
- XSS: escape subject, body, and ctaHref in buildEmailHtml() via escapeHtml()
to prevent HTML injection through user-controlled params (actor, preview, etc.)
- Encrypt webhook URLs at rest: apply maybe_encrypt_api_key on save
(settingsService for user URLs, authService for admin URL) and decrypt_api_key
on read in getUserWebhookUrl() / getAdminWebhookUrl()
- Log failed channel dispatches: inspect Promise.allSettled() results and log
rejections via logError instead of silently dropping them
- Log admin webhook failures: replace fire-and-forget .catch(() => {}) with
.catch(err => logError(...)) and await the call
- Migration 69: guard against missing notification_preferences table on fresh installs
- Migration 70: drop the now-unused notification_preferences table
- Refactor: extract applyUserChannelPrefs() helper to deduplicate
setPreferences / setAdminPreferences logic
- Tests: add SEC-016 (XSS, 5 cases) and SEC-017 (SSRF, 6 cases) test suites;
mock ssrfGuard in notificationService tests
This commit is contained in:
@@ -563,8 +563,11 @@ function runMigrations(db: Database.Database): void {
|
||||
CREATE INDEX IF NOT EXISTS idx_ncp_user ON notification_channel_preferences(user_id);
|
||||
`);
|
||||
|
||||
// Migrate data from old notification_preferences table
|
||||
const oldPrefs = db.prepare('SELECT * FROM notification_preferences').all() as Array<Record<string, number>>;
|
||||
// Migrate data from old notification_preferences table (may not exist on fresh installs)
|
||||
const tableExists = (db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='notification_preferences'").get() as { name: string } | undefined) != null;
|
||||
const oldPrefs: Array<Record<string, number>> = tableExists
|
||||
? db.prepare('SELECT * FROM notification_preferences').all() as Array<Record<string, number>>
|
||||
: [];
|
||||
const eventCols: Record<string, string> = {
|
||||
trip_invite: 'notify_trip_invite',
|
||||
booking_change: 'notify_booking_change',
|
||||
@@ -602,6 +605,10 @@ function runMigrations(db: Database.Database): void {
|
||||
SELECT 'notification_channels', value FROM app_settings WHERE key = 'notification_channel';
|
||||
`);
|
||||
},
|
||||
// Migration 70: Drop the old notification_preferences table (data migrated to notification_channel_preferences in migration 69)
|
||||
() => {
|
||||
db.exec('DROP TABLE IF EXISTS notification_preferences;');
|
||||
},
|
||||
];
|
||||
|
||||
if (currentVersion < migrations.length) {
|
||||
|
||||
Reference in New Issue
Block a user