security: fix all Critical and High findings from FLAWS_REPORT audit
- Auth: pessimistic locking on login tokens and refresh token rotation, backup code attempt counter, rate limiting verification - Schema: unique constraints on business numbers, FK relations, unsigned/signed alignment, attendance duplicate prevention - Invoices/PDFs: DOMPurify sanitization, bounded queries in stats and alerts, VAT rounding, Puppeteer error handling - Orders/Offers: transactional parent+child creation, Zod NaN refinement, status enums, uniqueness checks - Projects/Files: path traversal protection, streamed uploads, permission guards, query param validation - Attendance/HR: duplicate checks, ownership validation, GPS restrictions, trip distance validation - Frontend: modal lock reference counting, XSS escaping in print HTML, ref mutation fixes, accessibility attributes Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -100,6 +100,9 @@ export default async function attendanceRoutes(
|
||||
|
||||
// --- action=balances: leave balance overview for all users ---
|
||||
if (action === "balances") {
|
||||
if (!authData.permissions.includes("attendance.admin")) {
|
||||
return error(reply, "Nedostatečná oprávnění", 403);
|
||||
}
|
||||
const yr = Number(query.year) || new Date().getFullYear();
|
||||
const data = await attendanceService.getBalances(yr);
|
||||
return reply.send({ success: true, data });
|
||||
@@ -107,6 +110,9 @@ export default async function attendanceRoutes(
|
||||
|
||||
// --- action=workfund: monthly work fund overview ---
|
||||
if (action === "workfund") {
|
||||
if (!authData.permissions.includes("attendance.admin")) {
|
||||
return error(reply, "Nedostatečná oprávnění", 403);
|
||||
}
|
||||
const yr = Number(query.year) || new Date().getFullYear();
|
||||
const data = await attendanceService.getWorkfund(yr);
|
||||
return reply.send({ success: true, data });
|
||||
@@ -114,6 +120,9 @@ export default async function attendanceRoutes(
|
||||
|
||||
// --- action=project_report: monthly project hours ---
|
||||
if (action === "project_report") {
|
||||
if (!authData.permissions.includes("attendance.admin")) {
|
||||
return error(reply, "Nedostatečná oprávnění", 403);
|
||||
}
|
||||
const yr = Number(query.year) || new Date().getFullYear();
|
||||
const data = await attendanceService.getProjectReport(yr);
|
||||
return reply.send({ success: true, data });
|
||||
@@ -185,6 +194,10 @@ export default async function attendanceRoutes(
|
||||
if (!id) return error(reply, "Missing id", 400);
|
||||
const record = await attendanceService.getLocationRecord(id);
|
||||
if (!record) return error(reply, "Záznam nenalezen", 404);
|
||||
const isAdmin = authData.permissions.includes("attendance.admin");
|
||||
if (record.user_id !== authData.userId && !isAdmin) {
|
||||
return error(reply, "Nedostatečná oprávnění", 403);
|
||||
}
|
||||
return reply.send({ success: true, data: record });
|
||||
}
|
||||
|
||||
@@ -294,6 +307,14 @@ export default async function attendanceRoutes(
|
||||
if ("error" in leaveParsed) return error(reply, leaveParsed.error, 400);
|
||||
const leaveBody = leaveParsed.data;
|
||||
|
||||
if (
|
||||
leaveBody.user_id != null &&
|
||||
leaveBody.user_id !== authData.userId &&
|
||||
!authData.permissions.includes("attendance.admin")
|
||||
) {
|
||||
return error(reply, "Nedostatečná oprávnění", 403);
|
||||
}
|
||||
|
||||
const result = await attendanceService.createLeave(
|
||||
{
|
||||
user_id: leaveBody.user_id,
|
||||
@@ -342,6 +363,14 @@ export default async function attendanceRoutes(
|
||||
if ("error" in stdParsed) return error(reply, stdParsed.error, 400);
|
||||
const body = stdParsed.data;
|
||||
|
||||
if (
|
||||
body.user_id != null &&
|
||||
body.user_id !== authData.userId &&
|
||||
!authData.permissions.includes("attendance.admin")
|
||||
) {
|
||||
return error(reply, "Nedostatečná oprávnění", 403);
|
||||
}
|
||||
|
||||
const result = await attendanceService.createAttendance(
|
||||
{
|
||||
user_id: body.user_id,
|
||||
@@ -364,6 +393,8 @@ export default async function attendanceRoutes(
|
||||
},
|
||||
authData.userId,
|
||||
);
|
||||
if ("error" in result)
|
||||
return error(reply, result.error!, result.status ?? 400);
|
||||
|
||||
await logAudit({
|
||||
request,
|
||||
|
||||
Reference in New Issue
Block a user