Date: 2026-09-06
Do not open a public GitHub issue for a security report.
Use GitHub private vulnerability reporting on this repository.
Include:
- The affected URL or code path
- Steps to reproduce
- What you expect vs what happens
- Whether an account, application, or talent profile is involved
We will acknowledge valid reports and patch production before any write-up.
Lattice is a free Web3 career board: live roles from twenty crypto teams’ public Greenhouse, Lever, and Ashby JSON, plus talent, gigs, and a salary observatory. Production is lattice-devtechedge1.vercel.app. Search does not require an account. ATS pay is whatever the employer publishes (usually undisclosed here). Lattice does not invent a band. Contracts on /contracts are a demo state machine, not on-chain escrow and not a financial product. Not an offering. No wallet connect.
- https://lattice-devtechedge1.vercel.app and the
devtechedge/latticecodebase - Stored XSS via Markdown in role / gig / talent copy (including guest-posted listings)
- Auth bypass on bookmarks, applications, studio ATS, talent profiles, alerts, or contracts
- SQL injection in search, applications, or studio
- Leak of
DATABASE_URLor Better Auth secrets - Unauthenticated dump of another user's applications or talent profile marked
hidden/network - Open redirect or
javascript:links in Markdown orapplyUrl
- Rate limits on Vercel Hobby (in-memory, per-instance)
- Seed-catalog talent / gig copy being editorial, and ATS pay being undisclosed unless the employer publishes it
- The simulated gig/project contract flow (status changes only; no chain, no custody)
- Self-XSS
- Reports that require physical access to the operator's Vercel / GitHub account
- Third-party apply URLs after the visitor leaves this origin
- Parameterized SQL only (
getSqltagged templates; no string-concatenated queries) - Authed server functions use
authMiddleware; rows are scoped by the verifiedcontext.userId - Studio applications are limited to roles the signed-in user posted (
role_id IN (SELECT … posted_roles)) - Public talent directory returns only profiles with
privacy === "public" - Markdown links pass through
safeHref(http:/https:/ same-origin path).javascript:,data:, and protocol-relative URLs render as text applyUrlon a posted role must behttps:- Live ATS fetch is server-side only against
boards-api.greenhouse.io,api.lever.co, andapi.ashbyhq.com. Host allow-list,redirect: error, 12s abort, 10-minute in-memory cache. Greenhouse list fetches omit?content=true(detail can load bodies). Ashby lists useincludeCompensation=true. Apply URLs must be an ATS host or the company’s first-party careers host. ATS pay is only employer-published numbers. - Guest and authed listing payloads are size-capped (title, Markdown, arrays)
- Application stage updates accept a fixed enum
- Security headers via
vercel.json: CSP,nosniff,SAMEORIGIN, COOP, CORPsame-origin, Permissions-Policy (incl.interest-cohort=()), Referrer-Policy,X-Permitted-Cross-Domain-Policies: none, HSTS (max-age=63072000; includeSubDomains; preload) - Guest
publishRoleand anonymoussubmitSalaryuseguardPublicMutation(same-site Fetch-Metadata + per-IP in-memory rate limit) - Anonymous salary fields are Zod-bounded (finite amounts, year range); public posted lists capped at 200 rows
- No file resume upload; applications are form fields only
- No payments, no wallet connect, no seed phrases
- GitHub → Settings → General → Danger zone → Change visibility → Private.
- Confirm Vercel still has access (GitHub App installed on the private repo).
- Keep private vulnerability reporting enabled.
- Rotate any secrets that were ever pasted into chat or screenshots.
- Private source reduces copy-paste of the attack surface; it does not replace the controls above. The live site remains public.
| Name | Where | Why |
|---|---|---|
DATABASE_URL |
Vercel (Neon) | Persistent listings / applications. Without it, production falls back to embedded PGLite and reseeds on cold start. |
| Better Auth secrets | Vercel, when sign-in is on | Session cookies. Never commit them. |
Rotate any of the above if it was pasted into chat, a ticket, or a screenshot.
No public internet app is “unhackable.” Remaining limits:
- Guest posting is on.
/post/rolecan insert a listing withuser_idnull so a visitor can post without an account. That is product intent (free, non-paid tier) and also accepted spam / XSS residual risk. Markdown is sanitized; it is not a moderation queue. - Anonymous salary submissions are on (no account). Figures are not verified. Size-capped.
- CSP still allows
'unsafe-inline'scripts because of the theme boot + TanStack hydration, andhttps://grok.comfor the app-builder badge. - Vercel Hobby has no durable global per-IP rate limit across isolates (in-memory buckets are per-instance only).
- Counsel has not reviewed the legal drafts on
/termsand/privacy.
If you run a fork, set DATABASE_URL before exposing sign-in to the public internet.