Legal
Security
How authentication, authorisation, data handling and disclosure are actually implemented.
Authentication
Passwords are hashed with bcrypt at cost factor 12 and are never stored, logged or transmitted in a recoverable form.
Sign-in performs a hash comparison whether or not the address is registered, so response time cannot be used to discover who has an account. The same uniform message is returned either way.
Sessions are signed tokens backed by a server-side record, so a session can be revoked immediately rather than remaining valid until it expires. Changing your password ends every other session.
Sign-in, registration and password reset are rate limited per client address, and reset tokens are single-use and expire in one hour.
Authorisation
Route guards run in middleware as a fast path, but they are not the security boundary. Every protected page and every action re-checks against the session store, and every query for buyer data carries the owner’s identifier in the query itself.
That means a page which forgot to check still cannot read another buyer’s data, because the query cannot express it. Requests for a resource you do not own return 404 rather than 403 — confirming that something exists is itself a disclosure.
Data protection
Traffic is served over TLS. Client addresses in the audit trail are stored as salted hashes rather than in the clear, which answers the question an audit trail needs to answer without holding the address itself.
Uploaded images are validated on their actual bytes rather than their filename or declared type, then decoded and re-encoded. That removes embedded scripts, polyglot payloads and EXIF metadata — which routinely carries GPS coordinates a supplier did not intend to publish.
Exported files are generated on demand, marked uncacheable, and every export is recorded in the audit trail with the account that requested it.
Application hardening
A Content-Security-Policy is applied to every response, with a per-request nonce on authenticated and interactive routes. Framing is denied outright, content-type sniffing is disabled, and plugin content is blocked.
Form submissions and state-changing API calls are origin-checked. Session and visitor cookies are httpOnly and SameSite-restricted.
Input is validated against a schema at the server boundary, and rate limits apply to authentication, the AI endpoint, quotation submission and uploads.
The audit trail
Privileged actions — product edits, status changes, quotation pricing, sign-ins, session revocations and data exports — are written to an append-only log recording the actor, the action, the fields that changed, and a hash of the client address.
There is no edit or delete path anywhere in the application. Retention is a scheduled database job, so an operator cannot quietly remove an entry about themselves.
Reporting a vulnerability
If you believe you have found a security issue, report it to us before disclosing it publicly and allow reasonable time for a fix. Include enough detail to reproduce it.
Please do not run automated scanners against the production service, access or modify data that is not yours, or degrade the service for other users while testing. Good-faith research reported this way will not be pursued.
Questions about any of this? Get in touch.