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:
@@ -846,6 +846,59 @@ app.delete('/api/cves/:id', requireAuth(db), requireGroup('Admin', 'Standard_Use
|
||||
return res.status(403).json({ error: 'You can only delete resources you created' });
|
||||
}
|
||||
|
||||
// Cascade/compliance check for Standard_User
|
||||
if (req.user.group === 'Standard_User') {
|
||||
return db.all('SELECT id, exc_number FROM archer_tickets WHERE cve_id = ? AND vendor = ?', [cve.cve_id, cve.vendor], (archerErr, archerTickets) => {
|
||||
if (archerErr) { console.error(archerErr); return res.status(500).json({ error: 'Internal server error.' }); }
|
||||
|
||||
db.all('SELECT id, ticket_key FROM jira_tickets WHERE cve_id = ? AND vendor = ?', [cve.cve_id, cve.vendor], (jiraErr, jiraTickets) => {
|
||||
if (jiraErr && jiraErr.message && jiraErr.message.includes('no such table')) { jiraTickets = []; }
|
||||
else if (jiraErr) { console.error(jiraErr); return res.status(500).json({ error: 'Internal server error.' }); }
|
||||
|
||||
const allTickets = [
|
||||
...(archerTickets || []).map(t => ({ ...t, source: 'archer', key: t.exc_number })),
|
||||
...(jiraTickets || []).map(t => ({ ...t, source: 'jira', key: t.ticket_key }))
|
||||
];
|
||||
|
||||
if (allTickets.length === 0) {
|
||||
return doSingleCveDelete(req, res, id, cve);
|
||||
}
|
||||
|
||||
const likeConditions = allTickets.map(() => 'ci.extra_json LIKE ?');
|
||||
const likeParams = allTickets.map(t => `%${t.key}%`);
|
||||
|
||||
db.all(
|
||||
`SELECT ci.id, ci.extra_json FROM compliance_items ci
|
||||
JOIN compliance_uploads cu ON ci.upload_id = cu.id
|
||||
WHERE ci.status = 'active' AND (${likeConditions.join(' OR ')})`,
|
||||
likeParams,
|
||||
(compErr, compLinks) => {
|
||||
if (compErr && compErr.message && compErr.message.includes('no such table')) { compLinks = []; }
|
||||
else if (compErr) { console.error(compErr); return res.status(500).json({ error: 'Internal server error.' }); }
|
||||
|
||||
const hasLink = (compLinks || []).some(cl => {
|
||||
const json = cl.extra_json || '';
|
||||
return allTickets.some(t => json.includes(t.key));
|
||||
});
|
||||
|
||||
if (hasLink) {
|
||||
return res.status(403).json({
|
||||
error: 'CVE deletion blocked: associated ticket linked to compliance report',
|
||||
cascade_impact: { blocked: true, blocked_reason: 'Associated ticket is linked to a compliance report' }
|
||||
});
|
||||
}
|
||||
|
||||
return doSingleCveDelete(req, res, id, cve);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
doSingleCveDelete(req, res, id, cve);
|
||||
});
|
||||
|
||||
function doSingleCveDelete(req, res, id, cve) {
|
||||
// Delete associated documents from DB
|
||||
db.all('SELECT id, file_path FROM documents WHERE cve_id = ? AND vendor = ?', [cve.cve_id, cve.vendor], (docErr, docs) => {
|
||||
if (docErr) console.error('Error fetching documents:', docErr);
|
||||
@@ -892,7 +945,7 @@ app.delete('/api/cves/:id', requireAuth(db), requireGroup('Admin', 'Standard_Use
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ========== DOCUMENT ENDPOINTS ==========
|
||||
|
||||
Reference in New Issue
Block a user