Work / Tool Prototype
Shodan Analyst: internet exposure in plain English, counts not targets
Shodan Analyst is a proof-of-concept tool for understanding internet exposure at aggregate level. You ask a question or enter a CVE identifier, it builds the search for Shodan's index, and Claude writes a plain-English summary of the counts. It is read-only and built around guardrails against target lists.
Demonstration data. Scripted demonstration using invented data.
Ask Shodan Analyst “which SSH servers are visible on my own address block” and it builds the search, runs it and answers with a total, a table and a short summary. Shodan is an index of which services answer on which internet addresses. The same index that answers “how many of these exist?” will also answer “where are they?”, so I built the tool to stay on the first question: counts, countries and a summary, not a list of places to go and look.
Text version of the video
- The Explore page, with a question box, a Run button and the four modes in the sidebar.
- A question about SSH servers on "my own address block" is typed. The page shows the query it built, a total, a results table of invented hosts in documentation address ranges and a footer reminder.
- CVE Radar, with an invented identifier. The page shows invented advisory text, the query built from it, an exposure count and a chart by country.
- Sector / Protocol Watch with its default entry: a fixed query, a total and a chart by invented country code.
At a glance
- Status: Prototype.
- Platform: a local Streamlit web page, written in Python.
- Stack: Streamlit, the official Shodan Python library, the Anthropic Python library and the public NVD service for CVE records.
- Built with: Claude Code and Codex.
- AI inside the product: yes. A Claude model, named in the code as
claude-sonnet-5, turns questions into search queries and writes the summaries.
Four modes, one job each
There are four modes, each one a short module with a single job.
Explore takes a free-text question, has Claude translate it into Shodan’s search syntax, runs the search and shows the query, the total, a summary and a table of up to 20 results (host, port, product, country, organisation, last seen).
CVE Radar takes a CVE identifier, the standard public ID for a software vulnerability. It fetches the advisory text, asks Claude for a query matching the affected product, counts the matches and tallies the countries in a result sample. The summary describes the issue’s “blast radius” in general terms.
Sector / Protocol Watch has a fixed list of service types, such as remote desktop or industrial protocols, and an optional country filter. It asks the index only for a count with a breakdown by country, then charts it.
Exposed-Data Risk Scan works the same way for a short list of data stores that are sometimes left open by mistake. It reads counts and version spread from the index and stops there. It never connects to any database it counts.



Pointing a search engine at the aggregate
A tool like this can drift towards a target finder without anyone deciding it should. The spec states the guardrails and the README repeats them: only passive lookup calls, nothing that connects to a host the index returns, no mode that takes a named organisation as the subject of a “risk report”, and a fixed footer on every result saying the data is passive and publicly indexed and is not authorisation to access any host shown.
Build decision. The guardrails are structural wherever I could make them so. Every Shodan call goes through one thin wrapper that exposes only search, count, host and account info, and no exploit search. Two of the four modes use fixed query lists, so the model cannot widen them. Where the model does write the query, it never sees more than the question or advisory text, and the narrative step has its own system prompt that forbids instructions for accessing a host and forbids target lists. These are design choices and prompt instructions, not guarantees, and a prompt can be argued with, so the structure carries more weight than the wording.
The second decision was how to make the translation from English to Shodan syntax dependable. Shodan’s product: filter matches only the string its own scanner extracted from a service banner, so a model asked for “a Citrix login portal” will confidently invent a product name that matches nothing. The query-writing prompt now says so, and steers the model towards title, certificate and port filters, with a worked example for each. For appliances and CVE-driven searches, the filter has to match what the banner actually says, not what the vendor calls the product.
Under the hood
I built Shodan Analyst with Claude Code and Codex as a Python application with a Streamlit front end, and put its limits into the structure rather than leaving them to the prompts.
The page is one Streamlit script. Each mode is a function that returns a plain dictionary (query, total, rows or breakdowns, narrative, footer), and the page only draws it, so the logic can be tested without a browser. There is no database and no stored state: each run is a fresh request. Secrets come from a local environment file that the project’s ignore list excludes.
| Layer | What I used |
|---|---|
| Interface | Streamlit, one script; each mode returns a plain dictionary and the page only draws it |
| Language | Python |
| Integrations | Shodan (official Python library, behind one thin wrapper), the public NVD service for CVE records |
| Runtime AI | Claude, named in the code as claude-sonnet-5: query writing and aggregate summaries |
| Data and storage | None: no database, no stored state, secrets in an ignored local environment file |
| Hosting | Local Streamlit page |
| Tests | Three offline checks of count and country-breakdown parsing |
The model sits at two points. In the query step it receives the question and a system prompt about filters. In the narrative step it receives aggregate figures, such as totals and country and version breakdowns, plus a short result sample in Explore. The wrapper has a single-host lookup function, but none of the four modes calls it.
The project’s three offline checks cover the parsing of counts and country breakdowns:
test_sector_watch_parses_facets passed
test_exposed_data_scan_parses_facets passed
test_cve_radar_counts_countries_from_matches passed
All tests passed.
What it shows
A search index can answer an exposure question without becoming a target list if the structure of the tool keeps it on aggregates: one wrapper, fixed queries where possible, no connection to anything the index returns, and a footer on every result. Shodan Analyst shows that plain-English access to an exposure index can stay at the level of counts, countries and summaries.