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

@@ -1,39 +1,55 @@
let showSessionExpiredAlert = false;
let showLogoutAlert = false;
let getTokenFn: (() => string | null) | null = null;
let refreshFn: (() => Promise<boolean>) | null = null;
let refreshPromise: Promise<boolean> | null = null;
class ApiState {
showSessionExpiredAlert = false;
showLogoutAlert = false;
getTokenFn: (() => string | null) | null = null;
refreshFn: (() => Promise<boolean>) | null = null;
refreshPromise: Promise<boolean> | null = null;
reset() {
this.showSessionExpiredAlert = false;
this.showLogoutAlert = false;
this.getTokenFn = null;
this.refreshFn = null;
this.refreshPromise = null;
}
}
const state = new ApiState();
export const resetApiState = (): void => {
state.reset();
};
export const shouldShowSessionExpiredAlert = (): boolean => {
if (showSessionExpiredAlert) {
showSessionExpiredAlert = false;
if (state.showSessionExpiredAlert) {
state.showSessionExpiredAlert = false;
return true;
}
return false;
};
export const setSessionExpired = (): void => {
showSessionExpiredAlert = true;
state.showSessionExpiredAlert = true;
};
export const shouldShowLogoutAlert = (): boolean => {
if (showLogoutAlert) {
showLogoutAlert = false;
if (state.showLogoutAlert) {
state.showLogoutAlert = false;
return true;
}
return false;
};
export const setLogoutAlert = (): void => {
showLogoutAlert = true;
state.showLogoutAlert = true;
};
export const setTokenGetter = (fn: () => string | null): void => {
getTokenFn = fn;
state.getTokenFn = fn;
};
export const setRefreshFn = (fn: () => Promise<boolean>): void => {
refreshFn = fn;
state.refreshFn = fn;
};
export const apiFetch = async (
@@ -42,7 +58,7 @@ export const apiFetch = async (
): Promise<Response> => {
let token: string | null = null;
try {
token = getTokenFn ? getTokenFn() : null;
token = state.getTokenFn ? state.getTokenFn() : null;
} catch {
// token retrieval failed
}
@@ -69,21 +85,22 @@ export const apiFetch = async (
credentials: "include",
});
if (response.status === 401 && refreshFn) {
if (response.status === 401 && state.refreshFn) {
try {
if (!refreshPromise) {
refreshPromise = refreshFn().finally(() => {
refreshPromise = null;
if (!state.refreshPromise) {
state.refreshPromise = state.refreshFn().finally(() => {
state.refreshPromise = null;
});
}
const refreshed = await refreshPromise;
const refreshed = await state.refreshPromise;
if (refreshed) {
token = getTokenFn ? getTokenFn() : null;
token = state.getTokenFn ? state.getTokenFn() : null;
if (token) {
headers["Authorization"] = `Bearer ${token}`;
}
const { signal, ...retryOptions } = options;
response = await fetch(url, {
...options,
...retryOptions,
headers,
credentials: "include",
});
@@ -100,7 +117,7 @@ export const apiFetch = async (
export const getAccessToken = (): string | null => {
try {
return getTokenFn ? getTokenFn() : null;
return state.getTokenFn ? state.getTokenFn() : null;
} catch {
return null;
}