This document describes how the repo addresses common web security expectations.
All sensitive API operations are enforced in the Go backend, not in the Next.js app.
- The browser UI may redirect to
/loginwhen no token is stored; that is only for user experience. Anyone can bypass the UI (DevTools, custom HTTP client, etc.). - Real protection: routes under
backend/routes/routes.gothat useauthG.Use(middleware.JWTAuth()). Requests without a validAuthorization: Bearer <JWT>receive 401 before any listing, interest, or offer handler runs. - Authorization (who may do what) is enforced in controllers, e.g.:
- Only the listing seller can mark sold, delete, or view interests/offers for their listing.
- Hostel-type checks restrict which listings a user can see or act on.
- Offer/interest rules (availability, not own listing, etc.) are validated in Go after identity is known from the JWT.
JWT signing uses JWT_SECRET from the environment (backend/utils/config.go), never from source code.
All PostgreSQL access uses parameterized queries via pgx ($1, $2, …). User input is passed as separate arguments, not interpolated into SQL strings.
- Do not build SQL with
fmt.Sprintf, string concatenation, or raw user input in the query text. - When adding features, keep using
Query,QueryRow,Exec, and transactions the same way as existing controllers.
Do not commit real secrets to Git.
backend/.env,frontend/.env,frontend/.env.localare listed in.gitignore.backend/.env.exampleandfrontend/.env.examplecontain placeholders only (e.g.change-me,USER:PASSWORD,your_cloud_name).- In production (e.g. Render), set secrets in the host’s environment / dashboard - see
render.yaml(JWT_SECRETusesgenerateValue: true; database URL comes from the managed database).
Frontend “public” env vars (NEXT_PUBLIC_*) are embedded in the client bundle. That is expected for the API base URL and for Cloudinary unsigned upload presets. Never put:
- Payment provider secret keys
- Database passwords
- JWT signing secrets
- Cloudinary API secret
in NEXT_PUBLIC_* variables. For Cloudinary, use an unsigned upload preset with tight limits in the Cloudinary dashboard; the preset name may be public, the account API secret must not be.
See DEPLOYMENT.md for where to configure DATABASE_URL, JWT_SECRET, CORS_ORIGIN, and optional SMTP variables without storing them in the repository.