Next.js, hosted on Vercel, contact form sent by email
November 2026
Scope
The source code and its dependencies
A test version of the site, online and then locally
Accessibility and the handling of personal data (code review)
Sample report based on real projects. Names have been anonymized, and some findings were added or altered to show the full range of possible results.
How to read this report
Fix first
A problem that puts your app or your users at risk right now.
Fix it before letting new users in, ideally this week.
Fix or monitor
A weak spot that isn't urgent, but could become a problem as time passes or traffic grows.
Schedule the fix over the next few weeks, or set up the monitoring we suggest.
Solid
A good practice already in place, checked during the audit.
Nothing to do, except keep it in place as the app evolves.
At a glance
4Fix first
17Fix or monitor
6Solid
Can I open my app to new users?
Not as it stands. The site works and is built on sound foundations: encrypted connections, an active firewall, and forms protected against mass submissions. But two problems expose it today, and the site is already live.
Once the two red security items are fixed, which takes a few hours with the prompts provided, you can keep welcoming visitors with peace of mind. The accessibility and GDPR part adds two simpler red items: a contact form that screen reader users can't get past after a typo, and a font loaded from Google. The orange items can then be scheduled, with no rush.
Where to start
A-01
The site's framework has a known critical flaw
A publicly known flaw that bots scan for automatically. A simple update fixes it.
A-02
The email service key is visible to every visitor
Anyone can send emails in your name. The key must be replaced, not just moved.
A-04
No size limit on contact form messages
The easiest way to inflate your bill and bury your real leads.
ACC-01
The form's send button stays greyed out without saying why, and errors aren't read out
The form is the only way to reach you: a blind visitor who makes a typo can't send their request.
How we ran the audit
The audit combines a developer reading the code with four professional tools, the same ones security teams use at large companies. The tools are quick to spot known issues; the human review sorts their results, weeds out false alarms, and finds what they can't see.
Tests on the running site were done on a test version, never on the production site, and without breaking anything.
Dependency scan
Lists every component the site relies on and checks it against public vulnerability databases. Also spots secret keys left in the code.
Code analysis
Goes through the code line by line looking for bugs, security flaws and fragile code, like a very thorough reviewer.
Live site test
Browses the site like a visitor would, then inspects the server's responses: browser protections, cookies, information leaks.
Known vulnerability scan
Runs thousands of targeted checks for known flaws and misconfigurations, the same way attackers' bots do.
What the audit found
Fix first 2
Fix firstA-01 · Components and dependencies
The site's framework has a known critical flaw
What it is
Your site runs on Next.js, the framework that builds its pages. The installed version (15.5.19) has several published flaws, two of which can, under certain conditions, let someone run code on the server without logging in. They go through the tool that resizes the site's images.
Why it matters
Bots start scanning for this kind of flaw as soon as it's published. If your setup is affected, a stranger could take over the server behind your site: change pages, read secret settings, or use the server to attack other sites.
How to fix it
Update Next.js to version 15.5.28 or later, then redeploy. Your own code stays the same: this is a component update.
How to check
Run the dependency scan again: the flaw should be gone. Then check that the site and the generator work as before.
Fix firstA-02 · Secrets and access
The email service key is visible to every visitor
What it is
The site sends contact form messages through an email service, using a secret key. That key was declared as "public" in the configuration, so it's included in the code every browser downloads. Anyone can read it in a few clicks.
Why it matters
With this key, a stranger can send emails from your domain: convincing fake messages to your customers, phishing campaigns, or spam that gets your domain blocked by email providers. And you pay for every email sent.
How to fix it
Revoke the key right away in the email service's dashboard and create a new one. Then declare it as a private variable, used only on the server, never in code sent to the browser.
How to check
Search for the new key in the site's code as the browser receives it: it shouldn't appear anywhere. Make sure the old key is now rejected by the service.
Fix or monitor 11
Fix or monitorA-03 · Components and dependencies
About fifteen other components need updating
What it is
Besides Next.js, the scan found 68 published flaws in 14 other components, including 1 critical and 35 high. Most of them are in axios (32 flaws), used to call other services, and basic-ftp (5 flaws, including the critical one).
Why it matters
Many of these components are pulled in indirectly by other components, and not every flaw can be exploited on your site. But they pile up: the longer you wait, the longer and riskier the update gets.
How to fix it
Update the affected components to their fixed versions in small batches, testing the site after each batch. Start with basic-ftp and axios.
How to check
Run the dependency scan again: high and critical flaws should drop to zero, apart from any that don't have a fix yet.
Fix or monitorA-04 · Application code
No size limit on contact form messages
What it is
The contact form accepts messages of any length, then sends them by email through a pay-per-use service.
Why it matters
A bot can send very long messages, nonstop. Your inbox fills up, your email bill climbs, and genuine messages from prospects get lost in the noise.
How to fix it
Limit message size on the server (for example 5,000 characters), reject empty or very short messages, and keep the rate limit already in place on this form.
How to check
Send an oversized message on a test version: it should be rejected with a clear message, and no email should go out.
Fix or monitorA-05 · Application code
A crafted message can slow down the contact form server
What it is
To check what visitors type, the code uses "regular expressions", rules for searching through text. One of them, on the server, is written in a way that lets a specially crafted text keep it busy for a very long time. Eight other rules of the same kind exist, but they run in the browser or on your blog posts, which only you write.
Why it matters
A handful of well-chosen submissions is enough to tie up the server and slow down, or even block, the form for everyone. It's a form of denial of service that needs no account and no special tools.
How to fix it
Rewrite the rule in a simpler form that can't backtrack, or replace it with a check that doesn't use a regular expression. Also cap the text length before checking it.
How to check
Submit a very long, very repetitive text on a test version: the response should stay instant. Run the code analysis again: the warning should be gone.
Fix or monitorA-06 · Site and server configuration
Protection against malicious scripts is only in watch mode
What it is
The site declares a Content Security Policy (CSP): a list of what the browser is allowed to load and run. It's set to report-only mode, so the browser reports violations but blocks nothing. It also allows scripts written directly into the pages.
Why it matters
This is the safety net that stops an injected script from running if a flaw ever slips through. In report-only mode, that safety net doesn't exist yet.
How to fix it
Switch the policy to enforcing mode, once you've checked it doesn't block anything legitimate. Later, stop allowing scripts written into the pages and use identified scripts instead.
How to check
In the browser's developer tools, the Content-Security-Policy header should appear (rather than Content-Security-Policy-Report-Only), with no console errors on the main pages.
Fix or monitorA-07 · Application code
A value typed into the generator is echoed back in the page
What it is
On the generator page, a value passed in the page address is written back into the page's code as is. The testing tool flagged it as a possible script injection, without confirming it.
Why it matters
If this value isn't cleaned up, a booby-trapped link could run code in the browser of whoever clicks it, looking like your site. Here the risk seems low, since the value appears limited to a yes-or-no choice, but it needs a human check.
How to fix it
Accept only the expected values for this parameter and ignore anything else. That removes the risk, however the value ends up being displayed.
How to check
Open the page with an unexpected value in the address: it shouldn't appear in the page's code.
Fix or monitorA-08 · Application code
A generator error can go unnoticed
What it is
In the code that generates documents, an operation started in the background isn't monitored: if it fails, nobody is told.
Why it matters
The day this operation fails, the error vanishes silently. Your users may get an incomplete result without you knowing, and the problem becomes very hard to track down.
How to fix it
Wait for the operation to finish or handle its failure explicitly, logging the error in the site's logs.
How to check
Run the code analysis again: the warning should be gone. When an error is simulated, a message should appear in the logs.
Fix or monitorA-09 · Site and server configuration
The firewall's attack rules don't block anything
What it is
Your host offers a firewall with ready-made rules against common attacks (injections, malicious scripts…). They're turned on, but in "log" mode: they record attacks without stopping them.
Why it matters
It's a free second line of defense that stops many automated attacks before they reach your code. In log mode, it doesn't protect you.
How to fix it
Review the firewall log over a few days to make sure no legitimate visits are caught, then switch the rules to blocking mode.
How to check
In your host's dashboard, the rules should show "Deny" rather than "Log".
Fix or monitorA-10 · Site and server configuration
Any website can read your site's responses
What it is
The server responds with a setting (Access-Control-Allow-Origin: *) that lets any other website read its responses from a visitor's browser.
Why it matters
For public pages, that's harmless. But if this setting ever applies to pages that return personal information, a malicious site could read it without the visitor knowing.
How to fix it
Keep this setting for content that genuinely needs it (fonts, shared images), and never apply it to server routes.
How to check
Check the headers of the server routes: Access-Control-Allow-Origin should be missing, or limited to your own domains.
Fix or monitorA-11 · Site and server configuration
Three browser protections are not turned on
What it is
Three security settings the server sends to the browser are missing: Permissions-Policy (access to camera, microphone and location), Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy (isolating your site from other tabs).
Why it matters
Each one closes a door you don't use. Their absence isn't a flaw in itself, but they limit the damage if another flaw is ever found.
How to fix it
Add these headers to the site's configuration, starting with Permissions-Policy, which won't break anything.
How to check
The headers should appear in the site's responses, and the main pages should work normally.
Fix or monitorA-12 · Quality and maintenance
No automated tests protect the site against regressions
What it is
The project has no automated tests: nothing checks, after each change, that the contact form or the generator still work.
Why it matters
Every new request to your AI can break an existing feature without anyone noticing. That's often what makes it feel like the AI is going around in circles.
How to fix it
Add a few tests on the journeys that matter most: sending the contact form and generating a document. You don't need to test everything to gain a lot of peace of mind.
How to check
A test command should exist, pass, and fail if you deliberately break the form.
Fix or monitorA-13 · Quality and maintenance
A few small code issues to tidy up
What it is
The code analysis found two minor issues: a function named after a reserved part of the language, and a style property placed where it has no effect. It also flagged an "insecure" random number: on closer inspection, it's only used to space out retries when sending an email. That's a false alarm, and there's nothing to fix.
Why it matters
None of these is serious on its own. But they make the code harder to read, for you and for your AI.
How to fix it
Fix them in one go during a cleanup session, without touching the rest of the code.
How to check
Run the code analysis again: both warnings should be gone. The random number warning can be marked as a false positive in the tool.
Solid 4
SolidA-14 · Site and server configuration
Sensitive forms are protected against mass submissions
What it is
The contact form and the document generator only accept a limited number of requests per minute from each visitor. Beyond that, requests are rejected.
Why it matters
This is what stops a bot from using your forms to send spam or blow up your costs. Many apps built with AI don't have this protection.
How to fix it
Nothing to fix.
How to check
Keep it as the app evolves: if you add a new form, ask for the same protection.
SolidA-15 · Site and server configuration
The host's firewall blocks scanning bots
What it is
During the audit, our vulnerability scanner was detected and blocked by the host's firewall within a few minutes. We had to run that test again on a local copy of the site.
Why it matters
Attackers' bots use the same techniques, so they get slowed down the same way. That's good news, with one caveat, explained in "What the tools can't see".
How to fix it
Nothing to fix.
How to check
Keep this protection on, and don't turn it off "just to test" without turning it back on.
SolidA-16 · Site and server configuration
Connections are encrypted and the basic protections are in place
What it is
The site only accepts encrypted connections (TLS 1.2 and 1.3) and requires browsers to use them (Strict-Transport-Security). It also prevents other sites from displaying it inside their pages (X-Frame-Options) and stops the browser from guessing file types (X-Content-Type-Options).
Why it matters
These settings protect your visitors from having their data intercepted and from fake sites imitating yours. They're often forgotten.
How to fix it
Nothing to fix.
How to check
Keep them if you change hosts or configuration.
SolidA-17 · Quality and maintenance
The code is well organized overall
What it is
The code analysis gives the top maintainability rating (A), with little duplicated code (6%).
Why it matters
Tidy code is cheaper to change, and your AI finds its way around it more easily: it reads less, makes fewer mistakes, and uses fewer credits.
How to fix it
Nothing to fix.
How to check
Keep it that way by asking your AI to reuse existing code rather than duplicate it.
Accessibility and GDPR
Accessibility
Can people use the site without a mouse, with a screen reader, with low vision or at high zoom? Target: WCAG 2.2 level AA.
Mostly. One issue blocks the contact form for blind visitors; the rest is a nuisance rather than a blocker.
Fix first 1
Fix firstACC-01 · Forms
The form's send button stays greyed out without saying why, and errors aren't read out
What it is
The "Send" button is disabled until every field is valid. A disabled button can't receive keyboard focus and explains nothing. Error messages appear in red under the fields, but they're neither linked to their field nor announced.
Why it matters
A blind visitor who mistyped their email can't find the send button, or hears it's "unavailable", with no explanation. They can't contact you.
How to fix it
Keep the send button always enabled. On click, if a field is invalid, show the errors, link them to their field and move focus to the first invalid field.
How to check
With VoiceOver (Cmd + F5 on a Mac), type an incomplete email and press Send: focus should return to the email field and the error should be read out.
Fix or monitor 3
Fix or monitorACC-02 · Colours
Several grey texts are too pale to read comfortably
What it is
The grey used for captions, notes and the footer only reaches 3.9:1 against the page background, where small text needs 4.5:1. The placeholder text in form fields is around 3:1.
Why it matters
People with low vision, older people, and anyone on a phone in bright sunlight struggle to read these texts.
How to fix it
Switch to a darker grey from the theme that reaches at least 4.5:1, and darken the placeholder text.
How to check
Check each updated text with a contrast checker (for example WebAIM Contrast Checker): at least 4.5:1 against its actual background.
Fix or monitorACC-03 · Keyboard navigation
With the mobile menu open, keyboard focus slips behind the menu
What it is
The mobile menu covers the whole screen. After its last link, the Tab key moves on to the links of the page hidden behind it, and the Escape key doesn't close the menu.
Why it matters
People with low vision who zoom to 200% on a computer get the mobile menu: they lose their place on invisible elements.
How to fix it
While the menu is open, make the rest of the page inactive (the inert attribute), and close the menu with Escape, returning focus to the menu button.
How to check
Zoom to 200%, open the menu with Enter, tab past the last link, then press Escape: focus should stay in the menu, then return to the button.
Fix or monitorACC-04 · Forms
The form doesn't say which fields are required, and doesn't autofill
What it is
Nothing on screen says which fields are required. The name and email fields don't tell the browser what they expect either, so it can't offer to fill them in.
Why it matters
Everyone discovers the required fields through errors. People with motor impairments have to type everything by hand.
How to fix it
Add the note "All fields are required", plus the autocomplete="name" and autocomplete="email" attributes.
How to check
Click in the name field: the browser should offer the saved name.
Solid 1
SolidACC-05 · Structure
The page structure is clean
What it is
Language declared, distinct page titles, a single main heading per page, header, navigation, main and footer regions in place, images described.
Why it matters
A screen reader reads each page with the right accent, and a blind visitor can jump straight from one part to another.
How to fix it
Nothing to fix.
How to check
Keep these landmarks on future pages.
GDPR
What does the site do with its visitors' data, who does it share it with, and does the privacy policy say so honestly?
Yes, with conditions. The site doesn't track its visitors. One item needs a quick fix (the font loaded from Google); the others mostly mean completing the privacy policy.
Fix first 1
Fix firstRGPD-01 · Third parties
Every visit sends the visitor's IP address to Google, without saying so
What it is
The site loads its font through a direct link to Google's servers, instead of Next.js's built-in tool that hosts it on the site. Every visitor's browser therefore sends Google their IP address and the page visited. The privacy policy doesn't mention Google.
Why it matters
An IP address is personal data. A German court has already fined a site for this exact case (Munich, January 2022).
How to fix it
Load the font with Next.js's built-in tool, which downloads it once and serves it from your own site.
How to check
Open the site with the browser's Network tab: no request should go to fonts.googleapis.com or fonts.gstatic.com.
Fix or monitor 3
Fix or monitorRGPD-02 · Informing visitors
The privacy policy doesn't cover everything the site does
What it is
The policy names neither the host, nor the email service that forwards the form messages, nor the countries where these providers process data (the United States).
Why it matters
People must know, before sending their data, who receives it and in which country. An incomplete policy is the first thing a data protection authority looks at after a complaint.
How to fix it
Complete the policy with the list of providers, their role, their country, and how transfers outside the EU are safeguarded.
How to check
Reread the policy: every provider that receives data should be listed, with its country.
Fix or monitorRGPD-03 · Retention
No retention period is set for contact requests
What it is
Form messages arrive by email and stay with the email service and in your inbox indefinitely. The policy doesn't say how long they're kept.
Why it matters
The GDPR forbids keeping data longer than necessary. If a lead asks for their data to be erased, you need to know where to find it.
How to fix it
Pick a period (for example 3 years after the last contact), state it in the policy, and set up a yearly clean-up routine.
How to check
Once a year, the inbox should hold no unanswered request older than the chosen period.
Fix or monitorRGPD-04 · Transfers outside the EU
Form data is processed in the United States by default
What it is
The form runs at the host with no region selected, so in the United States by default, although the site targets French visitors.
Why it matters
A transfer outside the EU must be safeguarded and disclosed. Running the form in Europe narrows the issue down to a single provider, the email service.
How to fix it
Pick a European region (for example Paris) for the site's functions, and check the providers' commitments on transfers.
How to check
In the host's dashboard, the functions' region should show Paris (cdg1).
Solid 1
SolidRGPD-05 · Trackers
No cookies and no advertising trackers
What it is
No Google Analytics, no Meta pixel, no advertising cookies. The form only asks for what's needed, and its content isn't written to the server logs.
Why it matters
The site doesn't need a cookie banner, and visitors aren't tracked.
How to fix it
Nothing to fix.
How to check
Check this again before adding any analytics or advertising tool.
What the tools can’t see
A clean report doesn't prove the site is safe
On our first pass over the live version, the vulnerability scanner came back almost empty. Looking at the responses it actually got, we understood why: the host's firewall was serving it a block page, not your site. The tool had been testing… the block page.
That's why we always check what the tools actually saw, and why we ran that test again on a local copy of the site. An empty report without that check is worthless.
Tools get it wrong, both ways
Some warnings are false: a style rule flagged as a bug, for example, can be perfectly valid depending on where it's written. The reverse happens too: tools miss real flaws. The email service key visible to everyone (A-02) was found by reading the code, not by a tool.
Every finding in this report was therefore reviewed and rated by a developer, not copied from a tool.
Tools don't understand your business
A tool can recognize a known flaw. It can't tell whether a generated document contains the right information, or whether a visitor can get something they shouldn't by chaining normal steps together. Those questions require understanding what your site does.
Accessibility and GDPR can't be fully seen in the code
The accessibility part was done by reading the code: a full keyboard run and a listen with a screen reader are still to be done, and each finding explains how to test it in two minutes.
On the GDPR side, the contracts signed with providers and their settings (region, retention) can't be read in the code. This report is neither an accessibility certification nor legal advice.
A test version isn't production
Tests were run on a test version and a local copy, never on your production site. Host settings (firewall, headers) can differ between the two, so make sure the fixes are actually applied in production.
Glossary
Framework
The toolkit your site is built on (here, Next.js). It handles page rendering, addresses and the server.
Dependency
A component written by someone else and reused by your site. A modern site uses hundreds of them, often without you knowing.
Published flaw (CVE)
A flaw that has been found, documented and made public with an ID number. Attackers know about it too, so update quickly.
API key
A password that lets your site use an outside service (email, payments, AI). It must stay on the server.
Script injection (XSS)
An attack that runs malicious code in a visitor's browser while pretending to be your site.
CSP
Content Security Policy: the list of what the browser is allowed to load and run on your site.
CORS
The rules that decide which other websites can read your site's responses from a visitor's browser.
Security headers
Instructions the server sends to the browser with every page to turn on built-in protections.
Denial of service
An attack that steals nothing but makes the site slow or unavailable by keeping it busy or overloading it.
Web application firewall
A filter in front of your site, run by your host, that stops suspicious visits before they reach your code.
Rate limit
A maximum number of requests per visitor per minute, beyond which the site stops responding.
Automated test
A small program that checks, after every change, that a feature still works.
Screen reader
Software that reads the page aloud or in braille (VoiceOver on Apple, NVDA on Windows), used by blind people.
Contrast
The difference in brightness between a text and its background, from 1:1 (invisible) to 21:1 (black on white). Body text needs at least 4.5:1.
WCAG
The international web accessibility guidelines. Level AA is the usual target.
Processor
A provider that handles personal data on your behalf, such as the host or the email service.
Appendix: prompts for your AI
Paste each prompt as is into your AI tool (Lovable, Cursor, Claude…). Use them one at a time, in order, and check the result before moving on to the next.
P-01FixRelated to A-01
Update Next.js
My project uses Next.js 15.5.19, which has published critical security flaws (remote code execution through image optimization).
1. Update next to the latest 15.5.x release (at least 15.5.28), along with eslint-config-next if the project uses it, with the project's package manager.
2. Don't change any other package and don't modify the application code.
3. Run the build and fix only what the update breaks, explaining each change.
4. Finish with the list of modified files and the installed version.
Don't do a major upgrade (16.x) without asking me first.
P-02FixRelated to A-02
Make the email service key private
My email service key is declared with the NEXT_PUBLIC_ prefix, which exposes it in the code sent to the browser. I've already revoked the old key and created a new one.
1. Rename the variable without the NEXT_PUBLIC_ prefix everywhere it's used, and in .env.example.
2. Check that it's only read in server code (API routes, Server Actions), never in a client component.
3. Check that the .env* files containing secrets are ignored by git.
4. List the places you changed, and tell me which variable I need to update with my host.
Never write the key's value in the code or in your answer.
P-03FixRelated to A-03
Update the other vulnerable components
My project has dependencies with known flaws, including basic-ftp and axios.
1. List the vulnerable dependencies with the audit command of the project's package manager.
2. For each one, say whether it's a direct or indirect dependency, and give the fixed version.
3. Update in small batches: basic-ftp and axios first, then the rest. Run the build after each batch and stop if anything breaks.
4. Don't do any major upgrade without asking me.
Finish with a table: package, old version, new version, flaws fixed.
P-04FixRelated to A-04
Limit contact form messages
In the contact form API route, add server-side validation of the message:
- reject an empty message or one shorter than 10 characters;
- reject a message longer than 5,000 characters;
- return a 400 error with a clear message, without sending any email.
Add the same limit to the form in the browser to warn the user, but keep the server check as the source of truth. Don't change the existing rate limit. Add a test for each rejected case and one for a valid message.
P-05FixRelated to A-05
Make the contact form's regular expression safe
A regular expression in the contact form API route has super-linear complexity: a specially crafted text can keep it busy for a very long time (ReDoS).
1. Show me the expression and explain in one sentence what it checks.
2. Rewrite it so it can't backtrack excessively (no nested quantifiers, no overlapping alternatives), or replace it with a simpler check.
3. Cap the text length before applying the expression.
4. Add a test with a long, repetitive text that must be processed in under 50 ms, and tests proving the check accepts and rejects the same cases as before.
Don't touch the project's other regular expressions in this task.
P-06FixRelated to A-06
Turn on the Content Security Policy
My site sends a Content-Security-Policy-Report-Only header. I want to enforce it without breaking the site.
1. Show me where this header is defined and the current policy.
2. List what would be blocked if it were enforced as is (scripts, styles, images, fonts, outside services).
3. Suggest an enforced policy that allows only what the site needs. If possible, replace the 'unsafe-inline' allowance for scripts with nonces, which Next.js can generate.
4. Keep the report-only version running alongside for a while, to catch anything missed.
Don't apply anything before showing me the proposed policy.
P-07FixRelated to A-07
Accept only expected values in the generator
On the document generator pages, URL parameters (such as apport_nature) are reused in the page.
1. List every URL parameter these pages read.
2. For each one, define the exact list of allowed values, and ignore any other value (fall back to the default).
3. Check that none of these parameters is inserted into the page without going through React's normal rendering (no dangerouslySetInnerHTML, no hand-built HTML).
4. Add a test: an unexpected value must never appear in the page.
P-08FixRelated to A-08
Stop silent errors in the generator
In the generator API route, a promise is neither awaited nor handled (flagged by the code analysis).
1. Show me the line and what this operation does.
2. If the result is needed for the response, await it. Otherwise, add a .catch that logs the error with enough context to track it down, but no personal data.
3. Check whether there are other unhandled promises in the API routes, and list them without fixing them.
P-09MonitorRelated to A-09
Switch the firewall rules to blocking mode
Do this yourself in your host's dashboard, not with your AI:
1. Open the "Firewall" section, then the managed rules (injections, malicious scripts, code execution).
2. Review the last 7 days of logs and make sure no legitimate visits were recorded.
3. Switch each rule from "Log" to "Deny".
4. For a few days, keep an eye on the log and on messages from your visitors, in case a legitimate page gets blocked.
P-10FixRelated to A-10
Restrict which sites can read your responses
My site returns Access-Control-Allow-Origin: *.
1. Find where this header comes from: framework configuration, middleware, or the host's default setting.
2. List the affected routes, separating static files from API routes.
3. Suggest a configuration that removes this header from API routes, or limits it to my own domains, and keeps it only where it's needed.
Explain the impact to me before changing anything.
P-11FixRelated to A-11
Add the missing browser protections
Add these security headers to every page, in the project's existing header configuration:
- Permissions-Policy: camera=(), microphone=(), geolocation=();
- Cross-Origin-Opener-Policy: same-origin.
For Cross-Origin-Embedder-Policy, don't add anything yet: first explain what it might block (outside images, fonts or scripts).
Don't change the headers that are already there.
P-12FixRelated to A-12
Add the first automated tests
My project has no automated tests. I want to protect the two most important journeys.
1. Suggest the simplest testing tool for this project, and install it.
2. Write tests for the contact form API route: valid message, empty message, message too long. Replace the real email service with a fake one so no email goes out.
3. Write a test for generating a document, with typical answers.
4. Add a test script to package.json.
Keep the tests short and readable, and explain how to run them.
P-13FixRelated to A-13
Tidy up the small code issues
The code analysis found these minor issues. Fix them one by one, without changing anything else:
1. In app/error.tsx, the function is called Error, like the global object: rename it (for example ErrorPage).
2. In the global stylesheet, the font-display property is used outside an @font-face block, where it has no effect: move it or remove it.
Leave the Math.random() in the delay between email retries alone: it isn't used for security.
Finish with the list of changes.
P-14MonitorRelated to A-14
Keep the protection against mass submissions
Add this to your AI's permanent instructions (the project's rules file):
"Any new API route that sends an email, calls a paid service or writes data must have a per-visitor rate limit, like the existing contact and generation routes. If you create such a route, add this limit and tell me about it."
P-ACC-01FixRelated to ACC-01
Make the contact form understandable with a screen reader
In the contact form component, the send button is disabled until the form is valid, and the error messages aren't linked to their fields.
1. Remove the button's disabling while the user types; keep it only while sending.
2. Give each error message an id and add aria-describedby to the matching field, plus aria-invalid="true" when it's in error.
3. On submit, if a field is invalid: show all errors, then move focus to the first invalid field.
4. Check that Tab always reaches the send button, and that server-side validation is unchanged.
5. Don't change the look or behaviour of anything else, and list the files you changed.
P-ACC-02FixRelated to ACC-02
Darken the grey texts that are too pale
Several small grey texts (captions, notes, footer) and the form placeholders don't reach 4.5:1 against their background.
1. Find every grey class used for text smaller than 24 px.
2. Replace them with the theme's grey shade that reaches at least 4.5:1 against each text's actual background, and tell me the ratio you get.
3. Add a rule for the form placeholders (::placeholder) with a colour that reaches 4.5:1.
4. Don't change the look or behaviour of anything else, and list the files you changed.
P-ACC-03FixRelated to ACC-03
Keep focus inside the open mobile menu
When the mobile menu is open, the Tab key leaves the menu for the page hidden behind it, and Escape doesn't close the menu.
1. When the menu opens, add the inert attribute to the main content and the footer; remove it when the menu closes.
2. Close the menu with the Escape key and return focus to the menu button.
3. Add aria-expanded to the menu button, updated on every open and close.
4. Check with the keyboard, browser zoomed to 200%, that Tab only cycles through the menu and the top bar.
5. Don't change the look or behaviour of anything else, and list the files you changed.
P-ACC-04FixRelated to ACC-04
Mark required fields and turn on autofill
The contact form doesn't flag its required fields, and the name and email fields have no autocomplete attribute.
1. Show the note "All fields are required." under the form's introduction (or say which ones are optional).
2. Add autocomplete="name" to the name field and autocomplete="email" to the email field.
3. Check that the browser offers the saved name and email.
4. Don't change the look or behaviour of anything else, and list the files you changed.
P-RGPD-01FixRelated to RGPD-01
Serve the font from the site instead of Google Fonts
My Next.js site loads its font through a <link> tag to fonts.googleapis.com, which sends visitors' IP addresses to Google.
1. Replace this tag with next/font/google in the root layout: Next.js downloads the font at build time and serves it from my own site.
2. Keep the same weights and apply the font where it's applied today.
3. Remove any preconnect tag to fonts.googleapis.com or fonts.gstatic.com.
4. Search the project to confirm nothing calls googleapis or gstatic any more, then check the build passes.
5. Don't change anything else, and list the files you changed.
P-RGPD-02FixRelated to RGPD-02
Complete the privacy policy
My privacy policy names neither the host, nor the form's email service, nor the countries where they process data.
1. Add a "Recipients and processors" section listing each provider that receives data (host, email service), its role and its country.
2. Add an "International transfers" section explaining how these transfers are safeguarded (EU-US Data Privacy Framework or standard contractual clauses), with a "to be confirmed" marker where I need to check.
3. Update the policy's date.
4. Don't change anything else, and list the files you changed.
P-RGPD-03MonitorRelated to RGPD-03
Set a retention period for contact requests
To do by hand: pick a retention period for contact requests (for example 3 years after the last contact), then once a year delete older unanswered requests from your inbox and the email service.
Prompt for your AI:
1. Add a "Retention period" section to the privacy policy with the period I give you.
2. Create a docs/data-retention.md file describing where requests are stored and the yearly deletion routine.
3. Don't change anything else, and list the files you changed.
P-RGPD-04FixRelated to RGPD-04
Run the form in Europe
My Next.js site's functions run on Vercel with no region selected, so in the United States by default.
1. Add "regions": ["cdg1"] (Paris) to vercel.json, keeping the existing configuration.
2. Check that the build passes.
3. Don't change anything else, and list the files you changed.
To do by hand: check on dataprivacyframework.gov that the host and the email service are listed, and download their data processing agreements (DPA).
P-ACC-05Standing rule
Standing rule: accessible screens from the start
For every new screen or component on this site, follow WCAG 2.2 level AA:
- text at least 4.5:1 against its background, field borders and meaningful icons at least 3:1;
- real HTML elements (button, a, label, fieldset and legend) rather than divs with ARIA attributes;
- every field has a linked label, an autocomplete value for personal data, and its errors linked with aria-describedby; never disable a send button to signal an error;
- any message shown after an action goes through a role="status" or role="alert" region;
- focus visible on every background, and trapped inside any panel that covers the page;
- a single h1, no skipped heading levels, meaningful images with alt text describing the information;
- no font, script or image loaded from another site without telling me.