Enhance security and validation across multiple routes:
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
- Escape XML and HTML special characters to prevent injection attacks. - Implement rate limiting for various endpoints to mitigate abuse. - Add validation for email formats, password lengths, and field limits. - Ensure proper access control for recordings and room management.
This commit is contained in:
@@ -3,6 +3,7 @@ import { authenticateToken } from '../middleware/auth.js';
|
||||
import { getDb } from '../config/database.js';
|
||||
import {
|
||||
getRecordings,
|
||||
getRecordingByRecordId,
|
||||
deleteRecording,
|
||||
publishRecording,
|
||||
} from '../config/bbb.js';
|
||||
@@ -13,6 +14,25 @@ const router = Router();
|
||||
router.get('/', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const { meetingID } = req.query;
|
||||
|
||||
// M11: verify user has access to the room if a meetingID is specified
|
||||
if (meetingID) {
|
||||
const db = getDb();
|
||||
const room = await db.get('SELECT id, user_id FROM rooms WHERE uid = ?', [meetingID]);
|
||||
if (!room) {
|
||||
return res.status(404).json({ error: 'Room not found' });
|
||||
}
|
||||
if (room.user_id !== req.user.id && req.user.role !== 'admin') {
|
||||
const share = await db.get('SELECT id FROM room_shares WHERE room_id = ? AND user_id = ?', [room.id, req.user.id]);
|
||||
if (!share) {
|
||||
return res.status(403).json({ error: 'No permission to view recordings for this room' });
|
||||
}
|
||||
}
|
||||
} else if (req.user.role !== 'admin') {
|
||||
// Non-admins must specify a meetingID
|
||||
return res.status(400).json({ error: 'meetingID query parameter is required' });
|
||||
}
|
||||
|
||||
const recordings = await getRecordings(meetingID || undefined);
|
||||
|
||||
// Format recordings
|
||||
@@ -60,6 +80,14 @@ router.get('/room/:uid', authenticateToken, async (req, res) => {
|
||||
return res.status(404).json({ error: 'Room not found' });
|
||||
}
|
||||
|
||||
// H9: verify requesting user has access to this room
|
||||
if (room.user_id !== req.user.id && req.user.role !== 'admin') {
|
||||
const share = await db.get('SELECT id FROM room_shares WHERE room_id = ? AND user_id = ?', [room.id, req.user.id]);
|
||||
if (!share) {
|
||||
return res.status(403).json({ error: 'No permission to view recordings for this room' });
|
||||
}
|
||||
}
|
||||
|
||||
const recordings = await getRecordings(room.uid);
|
||||
const formatted = recordings.map(rec => {
|
||||
const playback = rec.playback?.format;
|
||||
@@ -97,6 +125,25 @@ router.get('/room/:uid', authenticateToken, async (req, res) => {
|
||||
// DELETE /api/recordings/:recordID
|
||||
router.delete('/:recordID', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
// M14 fix: look up the recording from BBB to find its meetingID (room UID),
|
||||
// then verify the user owns or shares that room.
|
||||
if (req.user.role !== 'admin') {
|
||||
const rec = await getRecordingByRecordId(req.params.recordID);
|
||||
if (!rec) {
|
||||
return res.status(404).json({ error: 'Recording not found' });
|
||||
}
|
||||
const db = getDb();
|
||||
const room = await db.get('SELECT id, user_id FROM rooms WHERE uid = ?', [rec.meetingID]);
|
||||
if (!room) {
|
||||
return res.status(404).json({ error: 'Room not found' });
|
||||
}
|
||||
if (room.user_id !== req.user.id) {
|
||||
const share = await db.get('SELECT id FROM room_shares WHERE room_id = ? AND user_id = ?', [room.id, req.user.id]);
|
||||
if (!share) {
|
||||
return res.status(403).json({ error: 'No permission to delete this recording' });
|
||||
}
|
||||
}
|
||||
}
|
||||
await deleteRecording(req.params.recordID);
|
||||
res.json({ message: 'Recording deleted' });
|
||||
} catch (err) {
|
||||
@@ -108,6 +155,25 @@ router.delete('/:recordID', authenticateToken, async (req, res) => {
|
||||
// PUT /api/recordings/:recordID/publish
|
||||
router.put('/:recordID/publish', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
// M14 fix: look up the recording from BBB to find its meetingID (room UID),
|
||||
// then verify the user owns or shares that room.
|
||||
if (req.user.role !== 'admin') {
|
||||
const rec = await getRecordingByRecordId(req.params.recordID);
|
||||
if (!rec) {
|
||||
return res.status(404).json({ error: 'Recording not found' });
|
||||
}
|
||||
const db = getDb();
|
||||
const room = await db.get('SELECT id, user_id FROM rooms WHERE uid = ?', [rec.meetingID]);
|
||||
if (!room) {
|
||||
return res.status(404).json({ error: 'Room not found' });
|
||||
}
|
||||
if (room.user_id !== req.user.id) {
|
||||
const share = await db.get('SELECT id FROM room_shares WHERE room_id = ? AND user_id = ?', [room.id, req.user.id]);
|
||||
if (!share) {
|
||||
return res.status(403).json({ error: 'No permission to update this recording' });
|
||||
}
|
||||
}
|
||||
}
|
||||
const { publish } = req.body;
|
||||
await publishRecording(req.params.recordID, publish);
|
||||
res.json({ message: publish ? 'Recording published' : 'Recording unpublished' });
|
||||
|
||||
Reference in New Issue
Block a user