fix(security #10): sanitize error details to prevent API key leakage
Added server/utils/sanitizeError.js which redacts: - ?apikey= query parameters (SABnzbd passes key in URL) - ?token= query parameters - X-Api-Key / X-MediaBrowser-Token / X-Emby-Authorization header values if they appear in the error message string Applied to all catch blocks in emby.js, sabnzbd.js, sonarr.js, radarr.js, and dashboard.js. Internal error.message still logged server-side (unredacted) for debugging.
This commit is contained in:
@@ -7,6 +7,7 @@ const { mapTorrentToDownload } = require('../utils/qbittorrent');
|
||||
const cache = require('../utils/cache');
|
||||
const { pollAllServices, getLastPollTimings, POLLING_ENABLED } = require('../utils/poller');
|
||||
const { getSonarrInstances, getRadarrInstances } = require('../utils/config');
|
||||
const sanitizeError = require('../utils/sanitizeError');
|
||||
|
||||
const EMBY_URL = process.env.EMBY_URL;
|
||||
const EMBY_API_KEY = process.env.EMBY_API_KEY;
|
||||
@@ -612,7 +613,7 @@ router.get('/user-downloads', requireAuth, async (req, res) => {
|
||||
} catch (error) {
|
||||
console.error(`[Dashboard] Error fetching user downloads:`, error.message);
|
||||
console.error(`[Dashboard] Full error:`, error);
|
||||
res.status(500).json({ error: 'Failed to fetch user downloads', details: error.message });
|
||||
res.status(500).json({ error: 'Failed to fetch user downloads', details: sanitizeError(error) });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -686,7 +687,7 @@ router.get('/user-summary', requireAuth, async (req, res) => {
|
||||
|
||||
res.json(Object.values(userDownloads));
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to fetch user summary', details: error.message });
|
||||
res.status(500).json({ error: 'Failed to fetch user summary', details: sanitizeError(error) });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ const express = require('express');
|
||||
const axios = require('axios');
|
||||
const router = express.Router();
|
||||
const requireAuth = require('../middleware/requireAuth');
|
||||
const sanitizeError = require('../utils/sanitizeError');
|
||||
|
||||
const EMBY_URL = process.env.EMBY_URL;
|
||||
const EMBY_API_KEY = process.env.EMBY_API_KEY;
|
||||
@@ -16,7 +17,7 @@ router.get('/sessions', async (req, res) => {
|
||||
});
|
||||
res.json(response.data);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to fetch Emby sessions', details: error.message });
|
||||
res.status(500).json({ error: 'Failed to fetch Emby sessions', details: sanitizeError(error) });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -28,7 +29,7 @@ router.get('/users/:id', async (req, res) => {
|
||||
});
|
||||
res.json(response.data);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to fetch user details', details: error.message });
|
||||
res.status(500).json({ error: 'Failed to fetch user details', details: sanitizeError(error) });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -40,7 +41,7 @@ router.get('/users', async (req, res) => {
|
||||
});
|
||||
res.json(response.data);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to fetch users', details: error.message });
|
||||
res.status(500).json({ error: 'Failed to fetch users', details: sanitizeError(error) });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -62,7 +63,7 @@ router.get('/session/:sessionId/user', async (req, res) => {
|
||||
|
||||
res.json(userResponse.data);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to fetch user from session', details: error.message });
|
||||
res.status(500).json({ error: 'Failed to fetch user from session', details: sanitizeError(error) });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ const express = require('express');
|
||||
const axios = require('axios');
|
||||
const router = express.Router();
|
||||
const requireAuth = require('../middleware/requireAuth');
|
||||
const sanitizeError = require('../utils/sanitizeError');
|
||||
|
||||
const RADARR_URL = process.env.RADARR_URL;
|
||||
const RADARR_API_KEY = process.env.RADARR_API_KEY;
|
||||
@@ -16,7 +17,7 @@ router.get('/queue', async (req, res) => {
|
||||
});
|
||||
res.json(response.data);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to fetch Radarr queue', details: error.message });
|
||||
res.status(500).json({ error: 'Failed to fetch Radarr queue', details: sanitizeError(error) });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -29,7 +30,7 @@ router.get('/history', async (req, res) => {
|
||||
});
|
||||
res.json(response.data);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to fetch Radarr history', details: error.message });
|
||||
res.status(500).json({ error: 'Failed to fetch Radarr history', details: sanitizeError(error) });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -41,7 +42,7 @@ router.get('/movies/:id', async (req, res) => {
|
||||
});
|
||||
res.json(response.data);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to fetch movie details', details: error.message });
|
||||
res.status(500).json({ error: 'Failed to fetch movie details', details: sanitizeError(error) });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -53,7 +54,7 @@ router.get('/movies', async (req, res) => {
|
||||
});
|
||||
res.json(response.data);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to fetch movies', details: error.message });
|
||||
res.status(500).json({ error: 'Failed to fetch movies', details: sanitizeError(error) });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ const express = require('express');
|
||||
const axios = require('axios');
|
||||
const router = express.Router();
|
||||
const requireAuth = require('../middleware/requireAuth');
|
||||
const sanitizeError = require('../utils/sanitizeError');
|
||||
|
||||
const SABNZBD_URL = process.env.SABNZBD_URL;
|
||||
const SABNZBD_API_KEY = process.env.SABNZBD_API_KEY;
|
||||
@@ -20,7 +21,7 @@ router.get('/queue', async (req, res) => {
|
||||
});
|
||||
res.json(response.data);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to fetch SABnzbd queue', details: error.message });
|
||||
res.status(500).json({ error: 'Failed to fetch SABnzbd queue', details: sanitizeError(error) });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -37,7 +38,7 @@ router.get('/history', async (req, res) => {
|
||||
});
|
||||
res.json(response.data);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to fetch SABnzbd history', details: error.message });
|
||||
res.status(500).json({ error: 'Failed to fetch SABnzbd history', details: sanitizeError(error) });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ const express = require('express');
|
||||
const axios = require('axios');
|
||||
const router = express.Router();
|
||||
const requireAuth = require('../middleware/requireAuth');
|
||||
const sanitizeError = require('../utils/sanitizeError');
|
||||
|
||||
const SONARR_URL = process.env.SONARR_URL;
|
||||
const SONARR_API_KEY = process.env.SONARR_API_KEY;
|
||||
@@ -16,7 +17,7 @@ router.get('/queue', async (req, res) => {
|
||||
});
|
||||
res.json(response.data);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to fetch Sonarr queue', details: error.message });
|
||||
res.status(500).json({ error: 'Failed to fetch Sonarr queue', details: sanitizeError(error) });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -29,7 +30,7 @@ router.get('/history', async (req, res) => {
|
||||
});
|
||||
res.json(response.data);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to fetch Sonarr history', details: error.message });
|
||||
res.status(500).json({ error: 'Failed to fetch Sonarr history', details: sanitizeError(error) });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -41,7 +42,7 @@ router.get('/series/:id', async (req, res) => {
|
||||
});
|
||||
res.json(response.data);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to fetch series details', details: error.message });
|
||||
res.status(500).json({ error: 'Failed to fetch series details', details: sanitizeError(error) });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -53,7 +54,7 @@ router.get('/series', async (req, res) => {
|
||||
});
|
||||
res.json(response.data);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to fetch series', details: error.message });
|
||||
res.status(500).json({ error: 'Failed to fetch series', details: sanitizeError(error) });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user