2e688c8e52
The application source was untracked, so this commit brings it under version control together with fixes for the issues found while auditing it. Notable fixes: Authorization - Require a session and scope to senderEmail on /api/transfers/[id] (GET, DELETE) and .../resend. These were unauthenticated over an autoincrement id, so the ids could be walked to soft-delete any transfer, read sender/recipient metadata, or make the app mail arbitrary recipients. - Require a session on the legacy /api/send and /api/chunk-upload endpoints, and take the sender from the session rather than a request field so transfers cannot be posted as another user. Encryption - Replace the chunk encryption scheme. Every chunk was encrypted under one shared session IV with its auth tag discarded, which reuses the AES-GCM keystream (XORing two ciphertexts recovers plaintext without the key) and left the stored file undecryptable, surfacing to users as a wrong-password error. Chunks are now self-contained frames carrying their own random IV and auth tag, behind a magic+salt header. - Files written by the previous format now report UNSUPPORTED_FORMAT instead of a misleading password error. Download - Verify the password against the stored bcrypt hash before serving a file, and enforce expiresAt and DELETED/EXPIRED status. - Move the password from the query string into a POST body so it stays out of access logs and Referer headers. - Record a download only after successful authentication. - Decrypt frame by frame through a stream instead of buffering the whole file, and encode the Content-Disposition filename per RFC 5987. Data exposure - /api/download ran before the password prompt and returned the full transfer row, including absolute server file paths. It now returns only what the pre-password screen renders; filenames, message and recipient are withheld until /api/verify succeeds. Correctness - Fix BigInt handling that made /api/transfers and /api/transfers/[id] fail unconditionally (JSON.stringify cannot serialize BigInt, and seeding a BigInt reduce with 0 throws). - Fail loudly on a missing chunk during reassembly rather than silently writing a corrupt file. - Meter plan usage in plaintext bytes rather than on-disk encrypted size. Ignore /uploads: it holds runtime transfer payloads, not source. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
59 lines
1.6 KiB
JSON
59 lines
1.6 KiB
JSON
{
|
|
"name": "transfer-tribe",
|
|
"version": "0.1.0",
|
|
"private": true,
|
|
"scripts": {
|
|
"dev": "next dev",
|
|
"build": "next build",
|
|
"start": "next start",
|
|
"lint": "next lint"
|
|
},
|
|
"dependencies": {
|
|
"@auth/prisma-adapter": "^2.10.0",
|
|
"@prisma/client": "^6.10.1",
|
|
"@radix-ui/react-alert-dialog": "^1.1.14",
|
|
"@radix-ui/react-avatar": "^1.1.10",
|
|
"@radix-ui/react-checkbox": "^1.3.2",
|
|
"@radix-ui/react-dialog": "^1.1.14",
|
|
"@radix-ui/react-dropdown-menu": "^2.1.15",
|
|
"@radix-ui/react-label": "^2.1.7",
|
|
"@radix-ui/react-progress": "^1.1.7",
|
|
"@radix-ui/react-separator": "^1.1.7",
|
|
"@radix-ui/react-slot": "^1.2.3",
|
|
"@radix-ui/react-tooltip": "^1.2.7",
|
|
"bcrypt": "^6.0.0",
|
|
"class-variance-authority": "^0.7.1",
|
|
"clsx": "^2.1.1",
|
|
"date-fns": "^4.1.0",
|
|
"fflate": "^0.8.2",
|
|
"formidable": "^3.5.4",
|
|
"lucide-react": "^0.523.0",
|
|
"next": "15.3.4",
|
|
"next-auth": "^4.24.11",
|
|
"next-themes": "^0.4.6",
|
|
"node-mailjet": "^6.0.8",
|
|
"react": "^19.0.0",
|
|
"react-dom": "^19.0.0",
|
|
"react-dropzone": "^14.3.8",
|
|
"react-icons": "^5.5.0",
|
|
"sonner": "^2.0.5",
|
|
"tailwind-merge": "^3.3.1",
|
|
"uuid": "^11.1.0"
|
|
},
|
|
"devDependencies": {
|
|
"@eslint/eslintrc": "^3",
|
|
"@tailwindcss/postcss": "^4",
|
|
"@types/bcrypt": "^6.0.0",
|
|
"@types/formidable": "^3.4.6",
|
|
"@types/node": "^20.19.26",
|
|
"@types/react": "^19",
|
|
"@types/react-dom": "^19",
|
|
"eslint": "^9",
|
|
"eslint-config-next": "15.3.4",
|
|
"prisma": "^6.10.1",
|
|
"tailwindcss": "^4",
|
|
"tw-animate-css": "^1.3.4",
|
|
"typescript": "^5"
|
|
}
|
|
}
|