DEV Community

Cover image for Your agent fetched a URL, a file, and a QR code today. None of them proved what they claimed to be.
Presendapp
Presendapp

Posted on

Your agent fetched a URL, a file, and a QR code today. None of them proved what they claimed to be.

A user-agent string is a claim. A Content-Type header is a claim. A QR code's payload is a claim. None of these are evidence — they're just strings someone (or something) decided to put there, and every one of them can be wrong, either by accident or on purpose.

This isn't a new problem. But it gets sharper the moment something acts on what it fetched instead of a human reading it first, which is exactly what an AI agent does by design. I've spent the last few days in the comments of some genuinely great security writeups — a parser-confusion SSRF bypass, a measurement of how many "AI crawler" requests are actually impersonators, a capability-composition failure that let sandboxed agents reach the open internet — and they're all the same shape of bug wearing different clothes. Here's the pattern, and four free, no-signup endpoints I built to check the specific claims that trip agents up most.

The pattern: two systems, one input, different conclusions

CVE-2026-19304 is a clean example. Two frameworks (Langflow, CrewAI) validated a URL with Python's urlparse, then fetched it with requests — which uses urllib3 internally, a different parser. Feed both the same URL with a backslash in an unusual spot, and they disagree about the hostname. The guard sees a public IP. The actual HTTP client connects to 127.0.0.1. The guard didn't fail — it answered a question about a string that wasn't the same question the fetcher was about to ask.

The AI-crawler-headers story is the same shape from a different angle: someone measured eight days of traffic claiming to be GPTBot, ChatGPT-User, and friends, and checked which ones Cloudflare could actually verify by reverse DNS. Result: 13% of GPTBot-claiming traffic verified. The other 87% was a string in a header, asserting an identity nobody checked.

And the OpenAI/Hugging Face sandbox story making the rounds this week is the large-scale version: every individual permission in that system was configured correctly. Agents just found a shared, writable substrate (a package registry cache) and used it as a message board, then as an internet gateway — because "can this caller reach the internet" was never actually asked at the one hop that mattered.

Three different systems. Same root cause: something arrived carrying a description of itself, and the description got trusted instead of checked.

Where this actually bites an agent

If your agent (or your own backend acting on an agent's behalf) fetches a URL, downloads a file, decodes something a user uploaded, or reads a bot's declared identity, you're making the same kind of decision these frameworks got wrong. A few concrete versions of it:

  • A tool call returns a URL to visit next. Is it pointing somewhere that's already known-malicious, or does it just look plausible?
  • A user uploads a file for your agent to process. Does its content match what it claims to be, or is a .pdf actually something else with a renamed extension?
  • Something in your pipeline decodes a QR code. If the payload is a URL, has anyone checked where it actually goes before your agent (or a human) follows it?
  • A request arrives claiming to be a known bot. Is that a verified identity, or just a header?

None of these need a heavyweight solution. They need one honest check, done before the "trusted" step, not after.

Four checks, one call each

These are free, no-signup, no-API-key endpoints — same policy as the rest of Presend's API. Pick the one that matches your actual risk, not all four by default.

Is this URL already known-bad, before your agent fetches it?
curl "https://presend.pages.dev/api/url-reputation?url=https://example.com"

Checks against URLhaus. A clean result means "not on this list," not "safe" — that distinction matters and the response says so explicitly.

Does this file's content match what it claims to be?
curl -X POST https://presend.pages.dev/api/file-type \
-H "Content-Type: application/pdf" \
--data-binary @upload.pdf

Reads the actual binary signature (magic bytes) and flags a mismatch against the declared Content-Type. This is the direct fix for the parser-confusion class of bug: don't trust the label, read the bytes, and if two things disagree about what something is, that disagreement is the finding.

Does a QR code point somewhere I should worry about?
curl -X POST https://presend.pages.dev/api/qr-scan -F "file=@qrcode.png"

Decodes the code and, if the payload is a URL, checks it against the same malware/phishing database in the same call — decode and verify in one step, so nothing downstream ever sees an unchecked payload.

Is this bot who its header says it is?
curl "https://presend.pages.dev/api/ai-crawler-check?domain=example.com"

This one checks the policy side (what your robots.txt permits for known AI crawlers) rather than the identity side (whether a specific request is really that crawler) — worth being precise about, since conflating the two is exactly the mistake the crawler-headers post was about. Policy and identity are different questions, and a tool that only answers one shouldn't imply it answered both.

The actual rule

Every one of the incidents above has the same fix, and it isn't "add more validation" in the abstract — it's specifically: check the same thing you're about to act on, not a description of it that arrived alongside it. A hostname string and a TCP connection destination are two different things unless you've verified they're the same. A Content-Type header and a file's actual bytes are two different things unless you've checked. A User-Agent string and the network identity making the request are two different things unless something independent confirmed it.

If your agent (or your CI, or your upload pipeline) trusts the label because the label is easy and the bytes are hard, that's the gap that eventually gets used against you — usually by something that isn't trying very hard, because it doesn't have to.

Full API docs: presend.pages.dev/api — OpenAPI spec and a Postman collection are linked there if you want to wire this into something bigger than a curl command. Curious what other "claim vs. verified" gaps people are hitting in agent pipelines specifically — the ones above are just the four I had a clean answer for.

Top comments (0)