Subscribe to Running With Scissors

Hacking, policy, advocacy, and the sharp end of security research. Delivered to your inbox.

Free. No spam. Unsubscribe anytime.

Check your inbox

A confirmation link has been sent to your email.

What Vegas Told Us About lookup.disclose.io, and What We Changed

We took lookup.disclose.io to Black Hat and DEF CON 34 and asked people to break it. Here are the exact reports we got back, the fixes that shipped within days, the seven integrations that went from roadmap to public in the three weeks since, and who this thing turned out to be for.

Pen-and-watercolour drawing of a brown paper parcel tied with twine on a desk. A purple redirect band is pasted diagonally across its shipping label, overriding the original address.

We spent the first full week of August in Las Vegas for Black Hat USA and DEF CON 34, and we spent a lot of it putting lookup.disclose.io in front of people and asking them to break it.

They did. Here is what they found, what we changed in the days that followed, and what has shipped in the three weeks since, which turned out to be the bigger half of the story.

First, what the thing actually does

Twenty seconds, no narration. A real asset goes in, the engine works through its sources in the open, and a real reporting route comes out.

That is an uncached lookup, so you can watch the sources being checked. The real lookup took 19.6 seconds and the clip runs it at 4.9x, which is labelled on screen, because the honest version of that wait is twenty seconds and nobody watches a progress bar for twenty seconds.

The job is simple to state and annoying to do: you have found something, you know the asset, and you do not know who to tell. Put in a domain, an IP, a URL, a package, a repository, a GitHub org, a container, or a company name, and get back the disclosure route that actually applies, with the evidence that produced it.

Free. No login. No signup.

What Vegas told us

The useful feedback was not "cool tool." It was people putting in an asset they own or work on, getting the wrong answer back, and telling us exactly how it was wrong. Here is how those reports read, lightly redacted where they named a specific customer's host:

👎 https://github.com/vance-club: GitHub orgs should not be resolved to Microsoft

👎 powerdns.com: you got our bug bounty platform wrong. We stopped using H1 a long time ago

👎 <customer>.jfrog.io: hosted Artifactory instances shouldn't resolve to JFrog

Three different failures, one shared disease: the engine was reaching for the nearest big entity instead of admitting what it did not know, and it was not marking the difference between the two. A GitHub organization page fell through to GitHub, and from GitHub to Microsoft. A customer-named tenant on a vendor's SaaS domain got attributed to the vendor, who run the platform and have no authority over that customer's security. And a catalogue entry that had been true years ago outranked what the organisation was currently publishing about itself in its own security.txt.

None of these are cosmetic. Send a researcher to the wrong owner and the best case is that their report goes into a void. The realistic case is that it lands on a company with no authority over the asset, gets treated as unsolicited scanning, and the researcher gets a hostile reply for doing the right thing. Attribution errors in a disclosure-routing tool are not UI polish.

The one that stings most is the third. PowerDNS were telling the world where to report bugs, in the file the standard exists for, and we were talking over them with stale data of our own.

Other things surfaced in the same window:

  • npm lookups like npm:openclaw/openclaw were failing to resolve back to the owning organisation, so a package with a perfectly good disclosure route came back with nothing.
  • A repository with a SECURITY.md was not being treated as having a disclosure policy, which is exactly backwards.
  • google.com, hackerone.com and bugcrowd.com were reporting "no security.txt found" when all three publish one. That was our bug, in TLS identity handling, not theirs.
  • The interface itself was the other half of the problem. The search box said "Search anything," which tells nobody what to type. Results led with engine vocabulary like complete and partial instead of what a human needs to know. The loading state cycled through source names on a timer rather than reporting what the backend was really doing, which is a small lie that costs you the whole trust budget the first time someone notices.

What changed in the first ten days

All of this shipped to production between August 1 and August 11.

The common thread in the fixes is not "we added more sources." It is that the tool now marks its own guesses. Most of what you are about to read is the engine learning to say "I know this" and "I am assuming this" as two different things, out loud, where you can see it and override it.

Attribution. A one-segment GitHub profile URL is now resolved from GitHub's own API. If the account type is an organisation, that is the owner. If it is a person, their employer never becomes the owner, and neither does GitHub, and neither does Microsoft. Nothing is hardcoded, including the org named in the report.

Current beats catalogued. When an organisation publishes a live, unexpired security.txt naming the platforms it uses, that declaration now suppresses contradicting entries from third-party catalogues. Your own current statement about yourself wins. Every platform you declare stays visible, including several at once.

Tenants are not their landlords. A hostname under a recognised multi-tenant SaaS namespace is now treated as an asset hosted by a provider, not as the provider. The tenant-safe lookup path runs exact-host checks and coordinator routes only, and the platform operator shows up as hosting infrastructure rather than as the owner.

Packages and repositories. npm shorthand now hands off to the repository strategy and reaches the owning organisation. A fetched, non-empty SECURITY.md is now an explicit high-confidence disclosure policy in its own right, even when it contains no email and no external link, because the document itself is the policy.

security.txt. The TLS identity bug is fixed. The three sites above resolve correctly.

Reporting routes come first. Every routable result now opens with a single recommended action built from the top-ranked contact, with the route class stated plainly, before any of the supporting detail. Owner-authorised routes rank above inferred ones, above CNA routes, above CERT fallbacks. If no first-party route can exist, you get an honest coordination route instead of a shrug.

Show the working. There is now an attribution evidence graph you can expand on any result to see the observations, claims and decisions that produced the answer. It is collapsed by default, it loads only when you open it, and it is structurally incapable of changing the routing. It explains the answer; it never becomes the answer.

Honest interface. The hero says what the tool does in plain language. Results distinguish "Detected: domain" from "Assuming organization" from "Using: package" so you can see when we actually recognised something versus when we guessed, and you can override it. Result states use task language instead of engine states. The progress display now shows real backend stages and real completed-source counts. The fake rotating source names are gone.

Feedback is a first-class feature. Thumbs up and thumbs down are on every result, with structured reasons, and there is an inline "Suggest correction" flow on the attribution card where you can tell us the owner is wrong and propose the right one. It records against the lookup, it is privacy-safe, and it deliberately does not silently rewrite what the engine decided. A human reads it.

It survived the conference. We shipped NAT-aware rate limiting on August 1 specifically so a room full of people behind one venue IP would not throttle each other. That held.

What shipped after that

The conference fixes were all one idea: teach the engine to say "I know this" and "I am guessing this" as two different things. The three weeks since have been three different ideas, and they are the ones worth your attention if you looked at this in Vegas and put it down again.

The integrations stopped being promises. When the paragraph below was first written, most of the integration surface was a roadmap. It is now seven shipped clients, every one of them a thin wrapper over the same production API, so there is exactly one resolution engine behind all of them and no second-class answer depending on where you asked from:

  • dio-lookup, the Unix CLI and npm package. Newline-delimited stdin to JSONL, so it drops into a recon chain: subfinder -d example.com | httpx | dio-lookup
  • Caido plugin. Context menu and sidebar lookups.
  • Burp Suite extension. Montoya BApp, right-click a host.
  • OWASP ZAP add-on. The same thing for the open-source proxy.
  • Chrome extension. Disclosure posture of the tab you are looking at.
  • Nmap NSE script. Enriches public scan targets with ownership and reporting routes, refuses private targets, caps scan-wide requests, and keeps an optional API key out of the output.
  • Nuclei templates. Turn scan output into disclosure contacts.

An integration that quietly breaks is worse than one that never shipped, so there is now a CI job that monitors the contracts those clients depend on and fails the build before a deploy, rather than leaving you to discover it in a scan at midnight.

IPs and ranges became a real answer. This was the most-requested thing we heard, and it is now native: the engine reads the most-specific bootstrap-RDAP assignment across the regional registries before it falls back to broader WHOIS, origin-AS, transit or hosting evidence. It is deliberately sceptical while it does it. A registry's own entity does not become the operator, an opaque maintainer handle does not become an organisation, and one unconfirmed role mailbox is not enough to name a company.

The interesting part is what it refuses to do. Look up 104.16.132.229 today and you get Cloudflare, high confidence, with an abuse route straight off the RDAP record. The result still comes back partial, and the headline still says "Responsible operator route found", because whoever operates the network at an address is a responsible route and not proof that they own the service running there. An operator route is genuinely useful. Calling it ownership would be a lie, and the status field is where we stop ourselves telling it.

Policies that were hiding are now reachable. Plenty of organisations do publish a disclosure policy and simply put it somewhere a fetcher cannot get to: behind bot defences, or six levels into a sitemap. Lookup now prioritises the legal, global and help branches of a sitemap and will follow an owner-endorsed security portal on the same organisational domain. Where a page genuinely cannot be fetched, a short-lived, human-reviewed registry can preserve its exact reporting route without ever guessing at ownership. That brought Bunnings, TCL and Motorola Solutions to their real published channels. A current security.txt or DNS Security TXT declaration still outranks every bit of it.

"Complete" got stricter. Publisher, maintainer, build host, identifier assignee, parent company and disclosure-platform relationships are context. They are often exactly the lead you want. None of them can quietly become ownership, and none of them can make a lookup complete any more. Only owner evidence with a scope-matched channel does that.

Ownership evidence got more sceptical generally. TLS attribution now reads only the verified leaf certificate, because issuer and intermediate CA identities could previously be parsed out of diagnostic output and promoted into "the organisation operating this asset." Privacy proxies and commercial registrants sitting on government namespaces no longer become owners. Certificate-incident contacts are labelled as certificate-incident routing rather than as a vulnerability-disclosure declaration.

Two bits of wording that were quietly lying. An empty owner result now says "No first-party reporting route found" rather than implying every route failed, because a CERT or CNA backstop is not nothing and should not be presented as nothing. And when the only options left are unverified convention addresses, security@ now comes before abuse@, which is the order the reporting intent actually implies.

Spikes fail fast instead of taking the service down. Duplicate searches join a single live lookup, the web API and the MCP server share one result cache, and work beyond capacity gets a clear, retryable busy response instead of piling into an unbounded queue.

Who this turned out to be for

The most useful thing the conference changed was not in the engine.

We built this for security researchers, and they are still the centre of it. What we kept running into in Vegas was people who were not researchers describing the identical problem in their own vocabulary. Same question every time: whose thing is this, and who do I tell? So we have written down who lookup is actually for, and started building for them on purpose.

  • Incident responders and threat intel teams. The dump holds credentials for forty other companies. The attacker staged on someone else's server. Every one of those is a notification you now owe somebody. Pipe the list in, get contacts back.
  • SOC, MDR and abuse desks. A compromised host is brute-forcing you or hosting the phish. Get the owner and the provider's abuse route, ranked, without leaving the playbook.
  • CERTs, CSIRTs and coordinators. Mass notification lives or dies on turning IPs and ranges into organisations with real inboxes, which is exactly why the RDAP work above got prioritised. You are in the graph too, as the backstop when nobody else has published a route.
  • Product security and AppSec teams. The bug is in a dependency, a partner's SDK, or a container you did not build. Follow it to the maintainer's actual security process rather than to the registry that happens to host it. Run your SBOM through it before you need to.
  • CNAs and bug bounty triage. The report landed in the wrong program. Send the researcher somewhere real instead of closing it as out of scope.
  • Attack surface, third-party risk and compliance teams. Is this host ours, a subsidiary's, or a lookalike? Does this vendor publish a security.txt at all? Evidence-backed answers with the confidence shown.
  • Anyone building automated or agentic tooling. There is no language model inside lookup, so it cannot invent a contact. Call the API, the CLI or the MCP server and get the same deterministic answer every time.

And one more, which is the one we would actually like you to do first: look yourself up. If a stranger cannot find you, neither can the person trying to warn you.

Did anyone use it?

Yes, and the shape of it was more interesting than the volume. We are deliberately not quoting a user count here, because every number available to us is measured at the edge and inflated by CDN and automation, and a caveated number is worse than no number.

What we can say cleanly is the direction. Lookup usage rose across the conference window while ordinary page traffic across the rest of the disclose.io estate was flat to slightly down. Con week moved tool usage, not reading. People were not browsing pages about disclosure, they were routing actual findings.

The other thing worth knowing, with the same caution applied: more raw requests now arrive at the MCP server than at the web form. We are wary of that number, including when we are the ones quoting it. Being listed in the MCP registry means a steady parade of directory crawlers introducing themselves, and handshake traffic is not somebody asking us a question. Our own code draws that line, counting a real tool call separately from everything else on the same endpoint, and the honest figure is a small fraction of the raw total. It is real, and it is growing, and that is why the machine-readable surface gets the same care as the web one. It is not the headline the raw number would let us write.

Three things we would like from you

Use it. lookup.disclose.io is free, needs no account, and is not going to try to sell you anything. There is a JSON API that is genuinely one-shot integrable, an llms.txt if you are pointing an agent at it, a hosted MCP server for Claude, Cursor and friends, and a pipe-friendly CLI in dio-lookup that drops straight into a recon chain: subfinder -d example.com | httpx | dio-lookup. The Caido, Burp Suite, OWASP ZAP, Chrome, Nmap and Nuclei clients listed above are all shipped and public. We wrote up the integration surface in more detail in Bring lookup.disclose.io Into Your Workflow, and the machine-readable version is always current in llms.txt. Higher API rate limits are free too, just ask.

Tell us what you need. This post exists because people told us we were wrong, specifically enough that we could do something about it within days. If you get a bad answer, hit 👎 and say why, or use "Suggest correction" on the attribution card. If you want an asset type we do not handle, a source we do not check, or an integration into whatever you already run, say so. Email hello@disclose.io, open an issue, or find us in the community forum. Wrong answers about your own organisation are the single most valuable thing you can send us, because you are the authority and we are not.

Share it. The reason a lookup fails is almost never that the engine is stupid. It is that the organisation on the other end has published nothing to find. Every researcher who uses this and every organisation that fixes its own record makes the next person's report land somewhere a human will read it. If you know someone who has ever given up on reporting a bug because they could not work out who to tell, send them this.

That is the whole project, really. Make the right thing to do the easy thing to do, and then keep fixing it in public.