fix: address all 11 review items for group-based access control

Bugs fixed:
- knowledgeBase.js: logAudit calls converted from positional args to object signature
- archerTickets.js: targetType/targetId renamed to entityType/entityId
- server.js: single CVE delete now has cascade/compliance check for Standard_User

Unprotected endpoints secured:
- ivantiTodoQueue.js: POST/PUT/DELETE now require Admin or Standard_User
- ivantiFindings.js: PUT note and POST sync now require Admin or Standard_User
- compliance.js: POST notes now requires Admin or Standard_User
- ivantiWorkflows.js: POST sync now requires Admin or Standard_User
- auth.js: cleanup-sessions now requires Admin via requireAuth + requireGroup

Additional fixes:
- ExportsPage.js: canExport() guard blocks Read_Only users
- knowledgeBase.js: Standard_User delete checks created_by ownership
- Migration: added INSERT/UPDATE triggers to enforce valid user_group values
This commit is contained in:
jramos
2026-04-07 09:52:26 -06:00
parent d910af847e
commit e9e2c0961d
10 changed files with 154 additions and 64 deletions

View File

@@ -132,16 +132,15 @@ function createKnowledgeBaseRouter(db, upload) {
}
// Log audit entry
logAudit(
db,
req.user.id,
req.user.username,
'CREATE_KB_ARTICLE',
'knowledge_base',
this.lastID,
JSON.stringify({ title: title.trim(), filename: sanitizedName }),
req.ip
);
logAudit(db, {
userId: req.user.id,
username: req.user.username,
action: 'CREATE_KB_ARTICLE',
entityType: 'knowledge_base',
entityId: String(this.lastID),
details: { title: title.trim(), filename: sanitizedName },
ipAddress: req.ip
});
res.json({
success: true,
@@ -232,16 +231,15 @@ function createKnowledgeBaseRouter(db, upload) {
}
// Log audit entry
logAudit(
db,
req.user.id,
req.user.username,
'VIEW_KB_ARTICLE',
'knowledge_base',
id,
JSON.stringify({ filename: row.file_name }),
req.ip
);
logAudit(db, {
userId: req.user.id,
username: req.user.username,
action: 'VIEW_KB_ARTICLE',
entityType: 'knowledge_base',
entityId: String(id),
details: { filename: row.file_name },
ipAddress: req.ip
});
// Determine content type for inline display
let contentType = row.file_type || 'application/octet-stream';
@@ -284,16 +282,15 @@ function createKnowledgeBaseRouter(db, upload) {
}
// Log audit entry
logAudit(
db,
req.user.id,
req.user.username,
'DOWNLOAD_KB_ARTICLE',
'knowledge_base',
id,
JSON.stringify({ filename: row.file_name }),
req.ip
);
logAudit(db, {
userId: req.user.id,
username: req.user.username,
action: 'DOWNLOAD_KB_ARTICLE',
entityType: 'knowledge_base',
entityId: String(id),
details: { filename: row.file_name },
ipAddress: req.ip
});
res.setHeader('Content-Type', row.file_type || 'application/octet-stream');
res.setHeader('Content-Disposition', `attachment; filename="${row.file_name}"`);
@@ -305,7 +302,7 @@ function createKnowledgeBaseRouter(db, upload) {
router.delete('/:id', requireAuth(db), requireGroup('Admin', 'Standard_User'), (req, res) => {
const { id } = req.params;
const sql = 'SELECT file_path, title FROM knowledge_base WHERE id = ?';
const sql = 'SELECT file_path, title, created_by FROM knowledge_base WHERE id = ?';
db.get(sql, [id], (err, row) => {
if (err) {
@@ -317,6 +314,11 @@ function createKnowledgeBaseRouter(db, upload) {
return res.status(404).json({ error: 'Article not found' });
}
// Ownership check: Standard_User can only delete articles they created
if (req.user.group === 'Standard_User' && row.created_by !== req.user.id) {
return res.status(403).json({ error: 'You can only delete resources you created' });
}
// Delete database record
db.run('DELETE FROM knowledge_base WHERE id = ?', [id], (err) => {
if (err) {
@@ -330,16 +332,15 @@ function createKnowledgeBaseRouter(db, upload) {
}
// Log audit entry
logAudit(
db,
req.user.id,
req.user.username,
'DELETE_KB_ARTICLE',
'knowledge_base',
id,
JSON.stringify({ title: row.title }),
req.ip
);
logAudit(db, {
userId: req.user.id,
username: req.user.username,
action: 'DELETE_KB_ARTICLE',
entityType: 'knowledge_base',
entityId: String(id),
details: { title: row.title },
ipAddress: req.ip
});
res.json({ success: true });
});