feat: secure combined uploads and update UI components
Changes: - Secure uploadUtilBillsProofOfPayment with checksum validation - Update ViewLocationCard to accept and use shareId prop - Update ViewBillCard to accept shareId and use it for uploads - Update ViewBillBadge to pass shareId to bill detail pages - Add client-side validation check for shareId before upload - Update back button links to use shareId Security improvements: - Both per-bill and combined uploads now validate checksum and TTL - IP-based rate limiting applied to both upload types - PDF magic bytes validation for both upload types 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,11 +11,12 @@ import { Pdf417Barcode } from "./Pdf417Barcode";
|
||||
import { uploadProofOfPayment } from "../lib/actions/billActions";
|
||||
|
||||
export interface ViewBillCardProps {
|
||||
location: BillingLocation,
|
||||
bill: Bill,
|
||||
location: BillingLocation;
|
||||
bill: Bill;
|
||||
shareId?: string;
|
||||
}
|
||||
|
||||
export const ViewBillCard: FC<ViewBillCardProps> = ({ location, bill }) => {
|
||||
export const ViewBillCard: FC<ViewBillCardProps> = ({ location, bill, shareId }) => {
|
||||
|
||||
const router = useRouter();
|
||||
const t = useTranslations("bill-edit-form");
|
||||
@@ -31,23 +32,28 @@ export const ViewBillCard: FC<ViewBillCardProps> = ({ location, bill }) => {
|
||||
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
// Validate file type
|
||||
|
||||
// Validate file type client-side (quick feedback)
|
||||
if (file.type !== 'application/pdf') {
|
||||
setUploadError('Only PDF files are accepted');
|
||||
e.target.value = ''; // Reset input
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!shareId) {
|
||||
setUploadError('Invalid upload link');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsUploading(true);
|
||||
setUploadError(null);
|
||||
|
||||
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('proofOfPayment', file);
|
||||
|
||||
const result = await uploadProofOfPayment(locationID, billID as string, formData);
|
||||
|
||||
|
||||
const result = await uploadProofOfPayment(shareId, billID as string, formData);
|
||||
|
||||
if (result.success) {
|
||||
setProofOfPaymentFilename(file.name);
|
||||
setProofOfPaymentUploadedAt(new Date());
|
||||
@@ -161,7 +167,7 @@ export const ViewBillCard: FC<ViewBillCardProps> = ({ location, bill }) => {
|
||||
}
|
||||
|
||||
<div className="text-right">
|
||||
<Link className="btn btn-neutral ml-3" href={`/share/location/${locationID}`}>{t("back-button")}</Link>
|
||||
<Link className="btn btn-neutral ml-3" href={`/share/location/${shareId || locationID}`}>{t("back-button")}</Link>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user