DEV Community

Surf
Surf

Posted on

Your agent can't safely read a web page it hasn't read yet

TL;DR

GET https://api.trustsource.cc/safefetch?url=<percent-encoded-url>
Enter fullscreen mode Exit fullscreen mode
  • You get back: sanitized page text plus a SAFE / REVIEW / BLOCK prompt-injection verdict.
  • Act on it: BLOCK → discard the content. REVIEW → use as data, never as instructions. SAFE → use normally.
  • Cost: $0.01 USDC per call via x402 on Base mainnet. No API key, no signup, no account.
  • To pay: the first call returns 402 with a PAYMENT-REQUIRED header. Sign the EIP-3009 authorization it describes and repeat the same request with an X-PAYMENT header.
  • Why bother: you can't ask your model whether a page is safe without first feeding it the page.

That's everything you need to call it. The rest explains how it decides.


The problem

You want to know whether a page is safe before your model reads it. But the usual way to find out is to ask the model — which means feeding it the page. If the page contains a prompt injection, you've already lost. The attack runs the moment the text enters the context window.

fetch() gives you no verdict. It hands your agent a string and hopes for the best.

What comes back

SafeFetch fetches the URL server-side, separates what a human would actually see from what's concealed in the markup, scans both, and returns clean text plus a verdict:

{
  "url": "https://example.com/",
  "verdict": "BLOCK",
  "risk": 0.95,
  "reasons": ["instruction override concealed in hidden content (matched in hidden content)"],
  "injection": {
    "detected": true,
    "techniques": ["instruction_override"],
    "findings": [
      { "technique": "instruction_override", "placement": "hidden", "severity": 0.95, "weight": 0.95 }
    ]
  },
  "content": { "text": "…sanitized page text…", "truncated": false }
}
Enter fullscreen mode Exit fullscreen mode

Your agent branches on one field instead of reasoning about raw HTML.

SAFE means nothing concealed and aggregate risk under 0.25. REVIEW means risk between 0.25 and 0.7, or a content type that couldn't be scanned, or a low-trust host. BLOCK means a critical technique found in hidden or comment placement, or risk ≥ 0.7.

Placement matters more than wording

This is the part that makes it usable in production.

A blog post about prompt injection contains the exact phrases an injection scanner looks for. A naive keyword filter flags every security article on the internet and becomes noise you learn to ignore.

SafeFetch weights each finding by where it was found — hidden 1.0, comment 0.95, accessibility 0.6, metadata 0.55, script 0.5, visible 0.2 — and caps visible-text hits in aggregate. So "ignore all previous instructions" printed in an article body scores low, while the same string in a display:none div scores high.

Concealment is the signal. Nobody hides text from humans for a benign reason.

What it detects

Eleven techniques: instruction_override, system_prompt_exfil, data_exfiltration, delimiter_spoof, encoded_payload, tool_call_bait, role_hijack, homoglyph_obfuscation, unicode_tag_smuggling, invisible_unicode, bidi_override.

That covers instructions buried in display:none elements, HTML comments and alt attributes; invisible Unicode-Tag (U+E0000) and zero-width smuggling; homoglyph and base64-encoded payloads; ChatML / [INST] delimiter spoofing; markdown-image exfiltration; and tool-call bait. Payloads split across several hidden elements are reassembled and scanned as one string, so chunking the attack doesn't evade it.

Paying for a call

The 402 response names the scheme (exact), the network (eip155:8453 — Base mainnet), the amount (10000 atomic units, i.e. $0.01 USDC), the USDC contract, and the address to pay.

The agent signs an EIP-3009 transferWithAuthorization for that exact amount, base64-encodes it, and repeats the identical request with an X-PAYMENT header. The facilitator settles on-chain and the endpoint returns 200.

Same URL twice, one extra header, one signature. No session, no nonce to track, and an abandoned 402 costs nothing. Any x402-aware HTTP client handles this for you.

Limits, honestly

  • 2 MB fetched, ~100,000 characters returned, 10-second timeout, 10-minute cache.
  • Redirects re-validated at every hop; DNS resolved then pinned to defeat rebinding; private and link-local ranges blocked.
  • Detection is heuristic, not exhaustive. A novel or heavily obfuscated payload can still read as SAFE. Treat SAFE as "no known pattern matched," not proof of safety — and keep treating fetched content as data rather than instructions.
  • Snippets inside injection.findings are defanged for display. They're evidence, not instructions.

Why it's worth a cent

One successful injection against an agent with tools, a wallet, or memory is worth considerably more than $0.01 to an attacker.

SafeFetch is one of seven verification endpoints at trustsource.cc — the others cover URL safety verdicts, email spoofability, domain trust, TLS certs, security headers, and AI-crawler policy.

Top comments (0)