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:
@@ -17,9 +17,10 @@ import QRCode from "react-qr-code";
|
||||
export interface ViewLocationCardProps {
|
||||
location: BillingLocation;
|
||||
userSettings: UserSettings | null;
|
||||
shareId?: string;
|
||||
}
|
||||
|
||||
export const ViewLocationCard: FC<ViewLocationCardProps> = ({ location, userSettings }) => {
|
||||
export const ViewLocationCard: FC<ViewLocationCardProps> = ({ location, userSettings, shareId }) => {
|
||||
|
||||
const {
|
||||
_id,
|
||||
@@ -47,13 +48,18 @@ export const ViewLocationCard: FC<ViewLocationCardProps> = ({ location, userSett
|
||||
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);
|
||||
|
||||
@@ -61,7 +67,7 @@ export const ViewLocationCard: FC<ViewLocationCardProps> = ({ location, userSett
|
||||
const formData = new FormData();
|
||||
formData.append('utilBillsProofOfPayment', file);
|
||||
|
||||
const result = await uploadUtilBillsProofOfPayment(_id, formData);
|
||||
const result = await uploadUtilBillsProofOfPayment(shareId, formData);
|
||||
|
||||
if (result.success) {
|
||||
setAttachmentFilename(file.name);
|
||||
@@ -121,7 +127,7 @@ export const ViewLocationCard: FC<ViewLocationCardProps> = ({ location, userSett
|
||||
<h2 className="card-title mr-[2em] text-[1.3rem]">{formatYearMonth(yearMonth)} {locationName}</h2>
|
||||
<div className="card-actions mt-[1em] mb-[1em]">
|
||||
{
|
||||
bills.filter(bill => (bill.billedTo ?? BilledTo.Tenant) === BilledTo.Tenant).map(bill => <ViewBillBadge key={`${_id}-${bill._id}`} locationId={_id} bill={bill} />)
|
||||
bills.filter(bill => (bill.billedTo ?? BilledTo.Tenant) === BilledTo.Tenant).map(bill => <ViewBillBadge key={`${_id}-${bill._id}`} locationId={_id} shareId={shareId} bill={bill} />)
|
||||
}
|
||||
</div>
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user