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:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user