- CLAUDE.md + AGENTS.md: canonical guiding principles for AI agents and developers (tenant isolation, no RLS, stack, security, conventions, environment gotchas) - SECURITY.md: required security controls (isolation, secrets, auth, input handling, least privilege) with VAPT status - GOVERNANCE.md: how changes are validated before merge — deterministic gates + AI review, risk tiers, human accountability - CONTRIBUTING.md: code hygiene, definition of done, file hygiene - docs/wiki/: product reference (overview, domain model, features, roles & access, API reference, glossary) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
80 lines
3.6 KiB
Markdown
80 lines
3.6 KiB
Markdown
# Security rules
|
|
|
|
Required security controls for TravelHub. These are not suggestions — a change
|
|
that breaks one is rejected. They exist because this is a **multi-tenant** system
|
|
holding traveller PII (names, passport numbers, contact details) where a single
|
|
leaked `WHERE` clause exposes every tenant.
|
|
|
|
Findings from the whitebox assessment and their status are in
|
|
[`security/VAPT-REPORT.md`](security/VAPT-REPORT.md).
|
|
|
|
## 1. Tenant isolation
|
|
|
|
- The tenant id is derived from the **authenticated identity**, never from the
|
|
URL, query string, header value, or request body. A client cannot choose or
|
|
forge the tenant it acts as.
|
|
- Every query against a tenant-owned table (`users`, `flights`, `bookings`,
|
|
`passengers`) filters on `tenant_id` as its first parameter. No exceptions.
|
|
- A miss within the tenant returns **404**, never 403 — existence of another
|
|
tenant's record must not be observable.
|
|
- **No PostgreSQL row-level security.** Isolation is enforced in the repository
|
|
layer by the rules above (see [CLAUDE.md](CLAUDE.md) for the rationale). At
|
|
scale, prefer schema- or database-per-tenant, not RLS.
|
|
|
|
## 2. Secrets
|
|
|
|
- **Never commit a secret.** `.env` and `*.env` are git-ignored; only
|
|
`.env.example` (placeholders) is tracked. Credentials, tokens and DB passwords
|
|
live in `.env` and are read at runtime.
|
|
- The `pre-commit` hook scans staged content for credential patterns and blocks
|
|
them. Do not bypass it (`--no-verify`) to commit a secret.
|
|
- **Never print or log a credential.** The e2e API-capture redacts the
|
|
`Authorization` header (it is the Basic-auth secret); keep it redacted in any
|
|
report or log.
|
|
- **If a secret is ever committed or exposed, rotate it immediately** — assume
|
|
it is compromised. Scrubbing history is not enough on its own.
|
|
|
|
## 3. Authentication & credential storage
|
|
|
|
- Passwords are hashed with **bcrypt in application code** at cost ≥ 12.
|
|
- The real hash and the not-found timing-dummy hash must use the **same cost** —
|
|
a mismatch creates a username-enumeration timing oracle (VAPT FINDING-2).
|
|
- 401 responses omit `WWW-Authenticate` (so the browser dialog doesn't fight the
|
|
SPA sign-in); a missing user still pays for a bcrypt comparison (timing).
|
|
- Authentication endpoints must be **rate-limited** before any public exposure.
|
|
|
|
## 4. Input handling
|
|
|
|
- **All SQL is parameterised** (`$1…$n`). Never concatenate a value into SQL.
|
|
Anything that must reach the SQL text (e.g. a sort column) is whitelisted.
|
|
- JSON request bodies use `DisallowUnknownFields` (blocks mass-assignment) and
|
|
are size-capped (`http.MaxBytesReader`).
|
|
- Validate and normalise free-text on input; treat the API as the trust
|
|
boundary, not the client.
|
|
|
|
## 5. Transport & least privilege
|
|
|
|
- Any non-local database connection uses **TLS** (`sslmode=verify-full`);
|
|
`disable` is only for loopback dev.
|
|
- The running service connects to PostgreSQL as a **least-privilege role**
|
|
(DML only), never a superuser. Migrations run under a separate, higher
|
|
privileged role the service never uses.
|
|
|
|
## 6. Dependencies & headers
|
|
|
|
- Run dependency scanning in CI: `govulncheck` (Go) and `npm audit` (frontend).
|
|
- Responses carry baseline security headers (`nosniff`, `X-Frame-Options`,
|
|
`Referrer-Policy`, `Cache-Control: no-store` on authenticated JSON, HSTS behind
|
|
TLS); the SPA ships a restrictive CSP.
|
|
|
|
## 7. Data handling
|
|
|
|
- Treat passenger PII as sensitive. Do not log it; do not put real PII in seed
|
|
or test data (use synthetic values).
|
|
- Reports (evidence, test) may contain synthetic test data only.
|
|
|
|
## Reporting a vulnerability
|
|
|
|
Raise security issues privately with the repository owner (do not open a public
|
|
issue). Include steps to reproduce and the affected files.
|