security: fix all Medium findings from FLAWS_REPORT audit
- Auth: TOTP replay protection with counter tracking, constant-time backup code comparison, atomic lockout increment, per-token logout - Invoices/PDFs: net-based VAT calculation, dangerous URL scheme stripping in cleanQuillHtml, orders-pdf error handling - Orders: reject item changes on status transition, cascading delete cleanup, take:1 with orderBy - Projects: atomic rename collision handling, MIME/extension validation, empty customer name rejection - Attendance: Czech public holiday awareness in frontend fund calculation, leave_hours 0 handling, invalid date NaN guard, bounded per-month queries in workfund - Users/Admin: profile audit logging + password validation, session revocation guard, session ID validation, dashboard DB aggregation, soft-deleted record protection in scope templates - Frontend: FormField label linkage, Pagination ARIA, error handling in OrderConfirmationModal, 401 propagation, GPS emoji hidden from screen readers, table sort state fix, geolocation race/abort cleanup, Leaflet popup DOM safety, Vehicles toggleActive minimal body, CompanySettings ref mutation fix, OfferDetail unlock abort, AttendanceBalances combined fetches - Utils: env validation, Puppeteer concurrency mutex, invoice alert cron cleanup on shutdown, body limit alignment, TOTP error logging, trustProxy from env, symlink rejection, rate cache Map usage Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -126,9 +126,12 @@ export default function Attendance() {
|
||||
action: string | null;
|
||||
}>({ show: false, action: null });
|
||||
const geoAbortRef = useRef<AbortController | null>(null);
|
||||
const mountedRef = useRef(true);
|
||||
const latestActionRef = useRef<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
mountedRef.current = false;
|
||||
if (geoAbortRef.current) geoAbortRef.current.abort();
|
||||
};
|
||||
}, []);
|
||||
@@ -179,6 +182,7 @@ export default function Attendance() {
|
||||
|
||||
const handlePunch = (action: string) => {
|
||||
setSubmitting(true);
|
||||
latestActionRef.current = action;
|
||||
|
||||
if (!navigator.geolocation) {
|
||||
alert.warning("GPS není dostupná");
|
||||
@@ -188,6 +192,7 @@ export default function Attendance() {
|
||||
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(position) => {
|
||||
if (!mountedRef.current) return;
|
||||
const { latitude, longitude, accuracy } = position.coords;
|
||||
submitPunch(action, { latitude, longitude, accuracy, address: "" });
|
||||
|
||||
@@ -203,6 +208,8 @@ export default function Attendance() {
|
||||
)
|
||||
.then((r) => r.json())
|
||||
.then((geoData) => {
|
||||
if (!mountedRef.current) return;
|
||||
if (latestActionRef.current !== action) return;
|
||||
if (geoData.display_name) {
|
||||
apiFetch(`${API_BASE}/attendance/update-address`, {
|
||||
method: "POST",
|
||||
@@ -219,6 +226,7 @@ export default function Attendance() {
|
||||
.catch(() => {});
|
||||
},
|
||||
(geoError) => {
|
||||
if (!mountedRef.current) return;
|
||||
let errorMsg = "Nepodařilo se získat polohu";
|
||||
if (geoError.code === geoError.PERMISSION_DENIED) {
|
||||
errorMsg = "Přístup k poloze byl zamítnut";
|
||||
|
||||
Reference in New Issue
Block a user