fix: audit fix pass #1 — all 19 verified HIGH findings + critical dep cleanup
Fixes every CRITICAL/HIGH finding from the 2026-06-09 full-codebase audit
(REVIEW_FINDINGS.md); each fix went through independent spec + code-quality
review. Plan and per-task log: docs/superpowers/plans/2026-06-10-audit-high-fix-pass.md
- attendance: schemas accept the combined local datetimes the forms/service
use (new dateTimeString helpers in schemas/common.ts), breaks persist on
create, AttendanceCreate submit rebuilt — every submit 400'd since 519edce
- 2fa: backup codes wired to /totp/backup-verify (+ remember-me parity),
enrollment QR generated locally via qrcode (CSP-blocked external service
also leaked the secret), dashboard shows per-user enrollment, not policy
- invoices/orders: per-line VAT survives re-saves (numberOr 0-respecting
coercion in formatters.ts), billing_text persists on update, issued-order
status transitions update UI gates
- trips: real pagination on all 3 pages, GET /trips/stats server aggregate
(shared buildTripsWhere + legacy distance coalesce), vehicle_id applies on
PUT with both-vehicle odometer recompute, print rebuilt (sync window.open,
escaped template, server totals)
- orders api: attachment_data PDF blob excluded from all non-binary reads
- warehouse: unit field is a Select over UnitEnum, receipt attachments
downloadable via new authenticated GET route
- downloads: shared RFC 5987 contentDisposition helper — Czech filenames no
longer 500 (warehouse, received-invoices, orders endpoints)
- misc: block-env hook actually blocks (exit 2 + stderr), project create
works with empty dates, NaN filter guards on trips endpoints
- deps: remove unused concurrently (clears both critical advisories), pin
@hono/node-server >=1.19.13 via overrides (clears the 3 moderates without
the Prisma 6 downgrade), drop deprecated @types stubs
Gates: tsc -b clean - vitest 30 files / 342 tests (31 new) - eslint 0 errors
- build OK
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,10 +4,12 @@ import { useQuery } from "@tanstack/react-query";
|
||||
import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import Link from "@mui/material/Link";
|
||||
import { useAlert } from "../context/AlertContext";
|
||||
import { useAuth } from "../context/AuthContext";
|
||||
import Forbidden from "../components/Forbidden";
|
||||
|
||||
import { apiFetch } from "../utils/api";
|
||||
import { formatCurrency, formatDate } from "../utils/formatters";
|
||||
import {
|
||||
warehouseReceiptDetailOptions,
|
||||
@@ -161,6 +163,33 @@ export default function WarehouseReceiptDetail() {
|
||||
}
|
||||
};
|
||||
|
||||
// A plain <a href> cannot send the Authorization header, so attachments are
|
||||
// fetched via apiFetch and handed to the browser as a temporary blob URL.
|
||||
const handleDownloadAttachment = async (att: WarehouseReceiptAttachment) => {
|
||||
if (!receipt) return;
|
||||
try {
|
||||
const response = await apiFetch(
|
||||
`/api/admin/warehouse/receipts/${receipt.id}/attachments/${att.id}`,
|
||||
);
|
||||
if (!response.ok) {
|
||||
const result = await response.json().catch(() => ({}));
|
||||
alert.error(result.error || "Nepodařilo se stáhnout přílohu");
|
||||
return;
|
||||
}
|
||||
const blob = await response.blob();
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = att.file_name;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
setTimeout(() => URL.revokeObjectURL(url), 60000);
|
||||
} catch (e) {
|
||||
alert.error(e instanceof Error ? e.message : "Chyba připojení");
|
||||
}
|
||||
};
|
||||
|
||||
const confirming = confirmMutation.isPending;
|
||||
const cancelling = cancelMutation.isPending;
|
||||
|
||||
@@ -249,13 +278,14 @@ export default function WarehouseReceiptDetail() {
|
||||
width: "45%",
|
||||
bold: true,
|
||||
render: (att) => (
|
||||
<a
|
||||
href={`/api/admin/warehouse/receipts/${r.id}/attachments/${att.id}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
<Link
|
||||
component="button"
|
||||
type="button"
|
||||
onClick={() => handleDownloadAttachment(att)}
|
||||
sx={{ textAlign: "left", font: "inherit" }}
|
||||
>
|
||||
{att.file_name}
|
||||
</a>
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user