This is a portfolio/demonstration project. Security fixes are applied to the main branch only.
Please do not open a public GitHub Issue for security vulnerabilities.
If you discover a security issue, email ktheodore517@gmail.com with:
- A description of the vulnerability
- Steps to reproduce
- Potential impact
- (Optional) a suggested fix
I will respond within 72 hours and aim to publish a fix within 7 days for confirmed issues.
- Passwords are hashed with bcrypt (cost factor 12) via passlib — plaintext passwords are never stored or logged.
- JWTs are signed with HS256 and expire after 60 minutes (configurable via
JWT_ACCESS_TOKEN_EXPIRE_MINUTES). - Tokens are validated on every authenticated request; revocation is handled by token expiry.
- File extension and MIME type are validated before writing to S3.
- Maximum file size is enforced at the API layer (default 50 MB).
- Files are stored in S3 with a UUID key — original filenames are never used as storage keys.
- The S3 bucket has public access fully blocked.
- Bucket contents are encrypted with AES-256 server-side encryption.
- IAM roles follow least privilege: Lambda can only read from
documents/*; the backend can only read/write/delete fromdocuments/*. - Download URLs are presigned and expire after 1 hour.
- Secrets (
JWT_SECRET_KEY,APP_SECRET_KEY,worker_api_key) must be provided via environment variables — no secrets are hardcoded. - Terraform state should be stored in an encrypted S3 backend in production (see commented-out block in
terraform/main.tf).
- No rate limiting on the auth endpoints. In production, add a reverse proxy or API Gateway throttling.
- JWT revocation requires waiting for token expiry. A production system would add a token blocklist.
- The local worker thread shares the app process; in production use Lambda for process isolation.