Dev.to Security 🔐 Cybersecurity 👁 0 📖 4 min read

We probed 171 third-party endpoints and 1,712 of our own handlers

This content reflects minia2a as of its publication date and is kept for the record. Current model: x402 pay-per-call in USDC on Base only, 5 free trial calls per signed wallet, no credits. We probed 171 third

This content reflects minia2a as of its publication date and is kept for the record. Current model: x402 pay-per-call in USDC on Base only, 5 free trial calls per signed wallet, no credits.

We probed 171 third-party endpoints and 1,712 of our own handlers. Here is what broke.

November 2026

A marketplace only measures what it decided to measure. Our delivery ledger records failures. It does not record "this request returned 200 OK, held the single-threaded sidecar for five seconds, and produced 80 MB". That second category is the one that hurts, and it is invisible in every error-rate dashboard.

This is a write-up of two audits we ran on our own catalogue: one over our handlers, one over the third-party listings we carry. We are publishing the method and the numbers, including the part where our own probing was wrong.

1. Our own handlers: the failure that does not show up in error rates

We scanned every handler for allocation sites whose size comes from the caller. Three stood out, all before fixes:

A loan amortisation endpoint answered a single request with 80,763,962 bytes in 5.33 s.

A depreciation endpoint answered with 10.9 MB.

A lorem-ipsum endpoint answered with 6.6 MB.

None of these is an error. All three return 200. They are the shape that makes a service look healthy while it is being used as a memory amplifier — and on a single-threaded sidecar, one such call delays everyone else behind it.

The first version of our static guard only looked at .repeat(), Array.from and new Array. It missed for loops entirely. Adding loops to the population took the scan from 25 handlers to 81, which is how the three above were found at all. The guard was not wrong; it was answering a narrower question than we thought.

After the fixes we re-ran the scan over all 1,712 handlers and libraries, then probed 140 candidates live — extreme value into each handler's first declared input field, measuring response bytes and wall-clock time. One endpoint was flagged:

x402-perp-funding-radar   asset=1000000   code=200   bytes=2,098   t=1.938s

That one is not amplification. It is slow because it actually queries four different venues. Both numbers are honest: two kilobytes, two seconds.

Result: zero unbounded allocation sites. We should state the limits of that claim. We only used the first declared field, and we probed 140 of 174 candidates. Multi-field amplification (rows times columns, for example) was not covered.

2. Third-party listings: what 171 unpaid probes found

We carried 171 active third-party listings at the time of the audit. For each one we sent a single unauthenticated request and sorted the outcome into four buckets. An endpoint answering 402 is a correct x402 endpoint and is not counted as a defect.

Cross-host redirect

The catalogue names one host, the endpoint serves another. None found.

Response > 2 MB

None found.

No payment gate

Answered 2xx without payment. Partly our fault — see below.

Dead

Timeout, 5xx, or unreachable.

The "free" bucket is mostly a probing error

Our first pass used GET for every endpoint. An endpoint that requires POST will happily answer 200 to a GET — that is its routing behaviour, not its price. Re-probing with a payment-shaped POST moved one of the ten to 402, which means it was a correct x402 endpoint all along.

At least one really does answer everyone without a payment gate, on both methods. But the honest summary is: a 200 from a GET probe is not evidence that a service is free. It is evidence that the route exists.

The dead ones are one operator, not twenty-nine

Twenty-nine dead listings sounds like a catalogue falling apart. Grouped by operator, they are not: one operator publishes six subdomains under a single domain, all answering 503, plus one unrelated host returning 530. That is one abandoned deployment, not twenty-nine independent failures.

A per-listing view hides exactly this. If you count dead entries without grouping them by who operates them, you will overestimate how sick your catalogue is — and you will under-estimate how much one abandoned operator costs you.

3. What we changed in our method

  • Probe with the method the catalogue advertises, not the one that is convenient. GET returning 200 proves nothing about price.
  • Measure bytes and wall time, not just status codes. A 200 that costs five seconds is a defect even though it never appears in an error rate.
  • Group dead endpoints by operator before reporting them as N findings. N is usually smaller than it looks, and the number that matters is how many operators.
  • State the coverage of your own scan. "Clean" over 140 of 174 candidates with one field each is a different claim from "clean", and the difference is the whole point.

We have not changed any third-party listing as a result of this audit. The dead ones are recorded, and delisting a batch of them is a decision we want to make slowly rather than in one sweep.

Originally published at minia2a.uk.

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.