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:
BOHA
2026-04-24 00:58:35 +02:00
parent 122eee175e
commit 528e55991b
57 changed files with 2355 additions and 1010 deletions

View File

@@ -254,10 +254,7 @@ export async function getStatus(userId: number) {
}
const worked = Math.round(workedHours * 100) / 100;
const holidayDays = monthRecords.filter(
(r) => (r.leave_type as string) === "holiday",
).length;
const adjustedFund = Math.max(0, (workingDays - holidayDays) * 8);
const adjustedFund = Math.max(0, fund);
const leaveHours = vacationHours + sickHours;
const covered = worked + leaveHours;
const remaining = Math.max(0, adjustedFund - covered);
@@ -266,7 +263,7 @@ export async function getStatus(userId: number) {
const monthlyFund = {
month_name: `${MONTH_NAMES[m]} ${y}`,
fund: adjustedFund,
business_days: workingDays - holidayDays,
business_days: workingDays,
worked,
covered,
remaining,
@@ -390,7 +387,11 @@ export async function updateAddress(
punchAction: string,
) {
const latest = await prisma.attendance.findFirst({
where: { user_id: userId },
where: {
user_id: userId,
departure_time: null,
arrival_time: { not: null },
},
orderBy: { created_at: "desc" },
});
if (!latest) return { error: "Nenalezen záznam" };
@@ -574,7 +575,6 @@ export async function getWorkfund(year: number) {
let worked = 0;
let vacationHours = 0;
let sickHours = 0;
let holidayDays = 0;
for (const rec of recs) {
const lt = (rec.leave_type as string) || "work";
@@ -591,8 +591,6 @@ export async function getWorkfund(year: number) {
vacationHours += Number(rec.leave_hours) || 8;
} else if (lt === "sick") {
sickHours += Number(rec.leave_hours) || 8;
} else if (lt === "holiday") {
holidayDays++;
}
}
@@ -1224,6 +1222,12 @@ export async function createLeave(data: LeaveData, authUserId: number) {
0,
),
);
const duplicate = await prisma.attendance.findFirst({
where: { user_id: userId, shift_date: shiftDate },
});
if (duplicate) {
return { error: "Pro zvolené datumy již existují záznamy docházky" };
}
await prisma.attendance.create({
data: {
user_id: userId,
@@ -1438,10 +1442,36 @@ export async function createAttendance(
data: CreateAttendanceData,
authUserId: number,
) {
const userId = data.user_id ?? authUserId;
const shiftDate = new Date(data.shift_date);
const startOfDay = new Date(
shiftDate.getFullYear(),
shiftDate.getMonth(),
shiftDate.getDate(),
);
const endOfDay = new Date(
shiftDate.getFullYear(),
shiftDate.getMonth(),
shiftDate.getDate() + 1,
);
const duplicate = await prisma.attendance.findFirst({
where: {
user_id: userId,
shift_date: { gte: startOfDay, lt: endOfDay },
},
});
if (duplicate) {
return {
error: "Pro zvolené datumy již existují záznamy docházky",
status: 400,
};
}
const record = await prisma.attendance.create({
data: {
user_id: data.user_id ?? authUserId,
shift_date: new Date(data.shift_date),
user_id: userId,
shift_date: shiftDate,
arrival_time: data.arrival_time ? new Date(data.arrival_time) : null,
arrival_lat: data.arrival_lat ?? null,
arrival_lng: data.arrival_lng ?? null,