The vulnerabilities agents write fresh
Ask an agent for a login flow and you tend to get a ?next= parameter redirected to whatever it contains. It is the obvious implementation, it is an open redirect, and the remedy was documented years before the model was trained.
Ask a coding agent to add "return to the page you came from" to a login flow.
Unless the framework hands it a guarded helper — Rails' redirect_back with a
fallback_location is one — here is the shape it tends to produce:
const next = req.query.next || "/dashboard";
// ... authenticate ...
res.redirect(next);
That is a correct implementation of the feature as described. It is also an open redirect. A static analyser tuned for it — Semgrep, CodeQL — will say so, if you run one; a unit test of the login flow will not, because the flow works.
What the attack actually is
The vulnerability is not that a URL gets rewritten. It is that your domain becomes the first hop of somebody else's phishing link.
An attacker sends a mail containing
https://yourbank.example/login?next=https://yourbank.example.attacker.com/login.
Everything a cautious person is trained to check passes. The link is on your
real domain. The TLS certificate is yours. The hostname in the address bar is
correct at the moment of the click. The victim logs in — genuinely, on your
site, against your real authentication — and is then bounced to a page that
looks exactly like yours and asks them to log in again "because something went
wrong". The second form is the attacker's.
The credibility comes from your domain, not from the destination, and every layer of defence that operates on the hostname is reading the first URL in the chain. Mail filters that score links by reputation see yours. Corporate proxies with an allowlist see yours. The first URL is legitimate, and it is the only one anything checks.
There is a second use that is quieter and often worse: the redirect is used as
an OAuth or SSO callback target, and the authorisation code, token or session
identifier travels in the query string or the Referer header to a host the
attacker controls. That is not a phishing page any more. That is credential
exfiltration with a valid signature on it.
This is also why "it's only a scanner finding" is the wrong reading. A scanner reports it as informational because the redirect on its own steals nothing. The severity lives in what it is combined with, and combining is the cheap part.
Why this class keeps coming back
Open redirect has been in the OWASP material for over a decade. The remedy is small, well documented, and in most stacks a helper somebody has already written. It has not been an interesting problem since about 2013. So why is it appearing in new code again, in volume?
A plausible reason is in what there is most of to imitate. The published writing on "redirect after login" is overwhelmingly tutorials and answers showing the form above, written to explain redirects rather than security. The writing on why that line put a company in an advisory is smaller and a different genre: post-mortems, CVE text, mailing list threads, none of it in the shape of a code example. The material available to imitate is lopsided, whatever a model does with it.
That points at where to look. The classes to worry about in generated code are not the exotic ones — a fast writer with a lot of examples is good at the common case. They are the solved ones, where the naive implementation is widely published, widely copied, and wrong.
| The feature as asked for | The obvious implementation | The class |
|---|---|---|
| "Send them back where they came from" | Redirect to a user-supplied parameter | Open redirect |
| "Let users embed a profile image by URL" | Server fetches the URL they give | Server-side request forgery |
| "Show the user's name on the page" | Interpolate it into the template | Cross-site scripting |
| "Let them filter by column" | Build the SQL from the parameter | Injection |
| "Log them in and keep them logged in" | Set the cookie, ship it | Missing Secure, HttpOnly, SameSite |
Every row has a documented remedy that predates the model, and every row is what a fluent, fast, unbothered writer produces first.
The fix, and why nobody writes it first
The correct version of the login redirect is boring:
- Accept only a path, never a full URL. If it does not start with a single
/, discard it. Reject//evil.exampleand/\evil.example, which are protocol-relative and resolve off-site. - Or better, do not take a URL at all. Take a key —
next=billing— and look up the destination in a table you control. - If you genuinely must allow external destinations, match the parsed host
against an allowlist. Not a prefix check on the string:
https://yoursite.com.attacker.comstarts withhttps://yoursite.com.
That version is rarely what gets written first, because the requirement never said any of it. The requirement said "return to the page you came from", and the extra lines serve a threat the ticket did not mention. Output that satisfies the stated requirement is exactly what was asked for.
Verification has to be automatic, because the writing already is
Here is the part that has actually changed. Human security review is built around a volume of code a person can read, and that is the assumption now under pressure. If a process catches this reliably at ten pull requests a week, it is worth asking honestly what it does at ten an hour. The likely answer is not that reviewers get worse. It is that they start sampling, and nothing records which files were skipped.
So the check has to run without being asked for, from outside, against the deployed thing.
HarpyWatch has a check called open-redirect. You give it the URLs
you care about — the login endpoint, the logout endpoint, anything that takes a
destination — and it requests them with the common redirect parameter names set
to an off-site host, then reads the Location header and any meta-refresh in
the body to see where a browser would be sent. If the answer is a host that is
not yours, that is the finding. Alongside it, cookie-flags reads
what your Set-Cookie headers actually carry, security-headers and
csp-policy report what a browser is told about framing and script origins,
and mixed-content finds the http subresource on the https page. None of that
is clever. It is the old list, asked again every few minutes, against the URL a
visitor uses.
The honest limit: an external check tests the surface a client can reach. It will find the redirect on your login endpoint. It will not read your source, and it cannot tell you that the same mistake exists on an internal admin route nobody links to. It is a smoke alarm, not an audit.
But the cheapest moment to find this is before the traffic arrives. Point the checks at the staging URL, before DNS moves. A redirect parameter that bounces to somebody else's host is a thirty-second fix in a branch and a disclosure email in production.
Cite this
The HarpyWatch team. “The vulnerabilities agents write fresh”. The Nest, HarpyWatch, 29 October 2026 UTC. https://harpywatch.com/blog/the-vulnerabilities-agents-write-fresh
More from The Nest
What thirty seconds of checking would have caught
A catalogue of small, boring, real failures — a www that resolves nowhere, a redirect that drops to http, a response that is not actually compressed — each one cheap to see from outside.
The certificate an agent provisioned and nobody agreed to renew
An AI agent that ran certbot once did the work correctly and made no commitment about the renewal. The setup is in your repository; the recurring obligation it created is in nobody's calendar.
The debt you take on when a customer tells you first
An outage costs what it costs. Being informed of it by the person it happened to costs something separate, it accrues, and it is charged against the next incident rather than this one.