Hevolve AI: Self-Evolving Multimodal AI Agents

Turn your domain expertise into AI agents that keep learning. Hevolve AI lets experts build multimodal AI systems by talking to them and correcting them in real time, with no code to write.

Key Features

Quick Links

© 2024 Hevolve AI Pvt Ltd. All rights reserved.

One unguarded call, 156 restarts, and a trivial denial of service

By Hevolve AI · 2026-07-21

What it looked like

The server is restarting occasionally, probably memory.

What it was

Any request to /% took the site down, on purpose or by accident.

The line
urlPath = decodeURIComponent((req.url || '/').split('?')[0]);

decodeURIComponent throws URIError on a malformed escape sequence. `/%`, `/%zz` and `/%c0%af` all qualify. Thrown from a request handler with no surrounding try/catch, it reached the top level and terminated the process.

Production had recorded 156 restarts. The supervisor dutifully brought the server back each time, which is exactly why it went unnoticed: the symptom was availability noise rather than an outage, and the metric it moved was restart count, which nobody was watching.

Why this one is embarrassing

It was introduced while improving that same server. The change added gzip and correct 404s -- genuine improvements, carefully reasoned -- and shipped a single-request denial of service alongside them. Scanners find `/%c0%af` by default; a bot was going to send it whether or not anybody was hostile.

The review energy went to the new behaviour and none to the pre-existing line the new behaviour ran after.

The fix and the general form
try {
  urlPath = decodeURIComponent((req.url || '/').split('?')[0]);
} catch (e) {
  return send(res, 400, 'Bad Request', ...);
}

Malformed input deserves a 400, not a stack trace. The deploy now probes `/%`, `/%zz` and `/%c0%af` after every release and fails the deploy unless each returns a 4xx and the server is still alive afterwards.

That last clause matters more than the status codes. Checking the response tells you the request was handled; checking that the process still answers afterwards tells you it survived handling it.
For an agent editing request handlers

Decoders throw. `decodeURIComponent`, `JSON.parse`, `Buffer.from(..., 'base64')`, date parsers, and every third-party equivalent will raise on input that a remote party fully controls. In an event-driven server there is often no frame between that throw and process exit.

When you touch a handler, enumerate the parsing it performs -- including the parsing that was already there. You are responsible for the whole function after you edit it, not just your diff.

The general lesson

Every value that arrives from outside your process is hostile input, including the parts of it you did not choose to parse.

availability
input-validation
regression

Other things that went wrong