NullBreach | AI-Powered AppSec Chat
The NullBreach | AI-Powered AppSec Chat ecosystem, an open-source, non-commercial application security portfolio. It brings together the app with a chat and OWASP-oriented code analysis, the presentation page, the MIT-licensed repository, and the API documentation.
By Valentina Ramírez
Published: June 16, 2026 · Updated: October 5, 2026
- Problem
- A security finding is of little use if it does not say how serious it is and how to fix it.
- Solution
- You paste a code snippet and get an analysis with severity, impact, and remediation, or ask in a security chat.
- Result
- In production and open source under the MIT license.
01 App
Next.js application with a security chat and OWASP-oriented code analysis. View detailsHide
- Next.js
- React
- TypeScript
- Tailwind CSS
- NextAuth
- Prisma
- PostgreSQL
- OpenAI API
- Vercel
The problem
A security finding is of little use if it does not say how serious it is, what its impact is and how to fix it. And a quick question about a code snippet rarely justifies setting up and tuning a rule-based scanner.
Findings without remediation
A list of patterns is not enough if it does not explain severity, impact and how to fix the risk.
Questions that get lost
A security question answered today is needed again tomorrow, and without history it has to be asked again.
AI that needs judgment
A model's answer can be wrong. It has to be presented as analysis assistance, not as the output of a deterministic scanner.
Results
- Chat and code analysis through the OpenAI Responses API, with limits of 4,000 and 20,000 characters applied before the model is called.
- Questions, answers and analyses stored per user in Prisma Postgres, with the chat history visible on screen.
- Open source under the MIT license, an API reference with Swagger UI and OpenAPI, and a health endpoint.
- Delivery with separate staging and production, migrations applied at deploy time and E2E tests against what was deployed.
View technical details OpenHide technical details
What I built
- 01
One repository with two surfaces: the public landing, a static Astro site (apps/landing), and the authenticated product in Next.js (App Router, React and TypeScript), with its frontend (app and components) and its backend (app/api, lib and prisma). There is a single deployment, which also serves the landing's static output.
- 02
Mounted under wavival.dev/nullbreach with Vercel Microfrontends: the portfolio is the default application and NullBreach is an independent child project. Next.js rewrites the public prefix and every navigation and API call goes through a single route helper (appPath).
- 03
Analysis and chat run on the server: the client sends the text to a route that requires a session, checks that it is not empty or over the limit (4,000 characters for a question, 20,000 for code, with a 413 above that) and only then calls the OpenAI Responses API. The OpenAI key and the model (an environment variable) live only on the server, in a single module.
- 04
The model instruction asks for practical advice grounded in OWASP; the analysis one asks to explain severity, impact and remediation. The answer is free text, rendered as plain text and not as HTML, and stored with the question or code that was sent.
- 05
Authentication with NextAuth: credentials (email and password with bcrypt, cost 12, 8 to 128 characters) and optional Google OAuth, with JWT sessions in HTTP-only cookies. A Google account is linked by normalized email.
- 06
Password recovery: a random token is generated, only its SHA-256 hash is stored with a one-hour lifetime and a single use, and the link is sent through Brevo. The response does not reveal whether the email exists.
- 07
Prisma ORM on Prisma Postgres with four models: users, chat history (question, answer and model), code analyses and recovery tokens. Deleting a user cascades to their records, and there are indexes by user and date. The on-screen history shows the last ten questions.
- 08
Authorization in two layers: a proxy rejects the private chat, analysis and history routes with a 401 before they run, and each route checks the session again. Response headers restrict framing, type sniffing, referrer leakage and unneeded browser permissions.
- 09
API documentation in docs/api.md, with a Swagger UI and an OpenAPI document served by the application itself, and a health endpoint that checks the database without calling OpenAI.
- 10
Gated delivery: work branches to dev, stg and main, a single path and always through a PR. GitHub Actions builds, applies migrations, deploys, checks health and runs Playwright against the deployment. Staging and production use separate Prisma Postgres databases and secrets, and automatic Git deployments are turned off.
- 11
CI on every PR: lint, formatting, types, Prisma validation, tests with coverage and build, plus commitlint, a high-severity dependency audit and a gitleaks secret scan.
Code analysis
Paste a snippet and get OWASP-oriented findings with severity, impact and remediation.
Security chat
Ask about vulnerabilities, controls and secure development and get a practical answer.
History
The last ten chat questions stay in view, saved to your account.
Account
Sign-up with email and password, Google sign-in and password recovery by email.
Bilingual landing
Public page in Spanish and English, indexable, with the private routes kept out of the index.
Documented API
Reference in docs/api.md, Swagger UI, an OpenAPI document and a health endpoint.
From snippet to finding
- 01 The user pastes a code snippet into the analyzer.
- 02 The proxy and the route check the session, that the text is not empty and that it is under 20,000 characters.
- 03 The server calls the OpenAI Responses API with instructions to analyze the code against OWASP and explain severity, impact and remediation.
- 04 The answer is stored as the user's analysis and returned to be shown as text.
Design
- 01
Terminal interface: panels with prompt commands like $ nullbreach scan ./src and monospaced type.
- 02
Landing with a single h1, ordered headings, navigation landmarks, a skip link and visible keyboard focus, with animations that respect prefers-reduced-motion.
- 03
Spanish and English routes with a canonical URL and language alternates. The sitemap lists only the two landings, and robots and metadata keep the API, sign-in and private area out.
- 04
Model answers are shown as text with line breaks, not as HTML, and errors use role alert with messages that do not reveal internal details.
Architecture decisions
01 Two surfaces in a single deployment ViewClose
- Context
- The product needs a public, indexable landing, an authenticated frontend and a backend with a database and an AI provider.
- Trade-off
- Splitting them into separate deployments adds contracts and operations a one-person project does not need yet. With Astro for the landing and Next.js for the product, each surface uses the tool that suits it, but their routes have to be kept in sync.
- Decision
- The landing is built with Astro and published as static files inside the Next.js deployment, which serves the exact landing routes and leaves the rest of the prefix to the product.
02 Mounted under wavival.dev with Microfrontends ViewClose
- Context
- The app has to live on the portfolio's domain without tying its deployment cycle to it.
- Trade-off
- The /nullbreach prefix forces microfrontends.json, the rewrites, the NextAuth basePath and every link and API call to stay in sync.
- Decision
- Vercel Microfrontends routes /nullbreach to the independent project, and a single route helper (appPath) builds every browser URL.
03 A language model as assistance, in one module ViewClose
- Context
- Reviewing code by hand takes security knowledge and time to explain impact and fixes.
- Trade-off
- The answer is free text and can be wrong: it is not scanner output and does not replace an expert review.
- Decision
- A single server module calls the OpenAI Responses API with OWASP-based instructions. The key never reaches the browser and size limits apply before the call and before storing.
04 NextAuth with credentials and Google ViewClose
- Context
- Each person should see only their own history and analyses.
- Trade-off
- JWT sessions hold no state on the server, and linking Google by email means trusting the email Google verified.
- Decision
- Credentials with bcrypt and optional Google OAuth, with a JWT session in an HTTP-only cookie. Data is always queried by the session user's id.
05 Password recovery with a single-use token ViewClose
- Context
- Someone who forgets their password needs to recover it without the response revealing which emails have an account.
- Trade-off
- It depends on an external email provider, and without it configured the link is only printed in development.
- Decision
- A random 32-byte token, only its SHA-256 hash stored, one hour of validity, a single use and the same response whether or not the account exists.
06 Authorization in two layers ViewClose
- Context
- The chat and analysis routes call a paid provider and write to the database.
- Trade-off
- Checking the session twice repeats a line in every route, but a change in the proxy does not leave a route open.
- Decision
- A proxy answers 401 to the private routes before they run and each route repeats the check.
07 A single gated delivery path ViewClose
- Context
- An application with AI keys and a database should not reach production without passing through staging.
- Trade-off
- Promotion PRs and deployed checks add steps, and the staging and production databases are kept apart.
- Decision
- Work branches to dev, stg and main, always through a PR. GitHub Actions builds, migrates, deploys, checks health and runs Playwright. Automatic Git deployments are off.
Learnings
A language model that reviews code should be presented as analysis assistance, not as deterministic verification.
Authenticating and limiting size before calling a paid provider controls cost and protects the database at the same time.
A static Astro landing can ride inside the application's deployment and keep its own SEO, without paying for two deployments.
Roadmap
Now
- Application in production with a security chat, code analysis, accounts and password recovery.
Next
- Show stored analyses and let users delete their history and account.
- Rate limits on chat and analysis, which call a paid provider.
- Verify the email of password accounts before Google can link to them.
02 Landing
Static, indexable Astro landing in Spanish and English. View detailsHide
- Astro
- TypeScript
- SEO
- Vercel
View technical details OpenHide technical details
What I built
- 01
Static Astro site inside the same repository (apps/landing), separate from the Next.js application. It serves the public routes /nullbreach and /nullbreach/en.
- 02
Content: product overview, technical limits, author information, and contact details, in Spanish and English.
- 03
Its own technical SEO: structured data, canonical URLs, robots directives, and a sitemap generated with @astrojs/sitemap.
- 04
Astro's static output is published inside the Next.js deployment, which serves the exact landing paths (/nullbreach, /nullbreach/en, sitemap, and robots) and leaves /nullbreach/:path* for the product routes.
- 05
The landing holds no runtime secrets. The authenticated product (sign-in, chat, analysis, and history) is not indexable.