Summary

Between 6 and 7 October 2026 we re-read the source of every interactive tool on this site (71 pages) and drove each one in a headless browser with generated test inputs. The pass recorded 308 findings: 29 critical, 83 high, 129 medium and 67 low. Twelve tools did not work at all. Every finding was fixed, every page was re-tested with zero script errors, and the per-page record is in the changelog. This article is the long version: what was broken, why it got through, and the checklist we now run so it does not happen again.

We are publishing it because almost nobody who runs a free-tool site does, and because the failure modes are not special to us. If you maintain browser-side OSINT tooling, the odds are good that several of the items below apply to you too.

Why we audited our own tools

Most of these tools were written quickly, by one person, with an AI assistant, over about a year. Each one worked when it shipped. What nobody did was go back. Third-party APIs changed shape, CDN packages moved, a deploy left worker files behind, and a CSS refactor hid a results panel that two tools depended on. None of that produces an error a visitor would report; it produces a tool that quietly returns nothing, and people leave.

The trigger was finding that our own Photo Forensics Studio had been failing to load its engine since at least August. If the flagship could be dead for seven weeks without anyone noticing, the rest needed checking too.

Takeaway: tools fail silently; you have to go looking.

How the audit was run

Eight reviewers each took a disjoint group of pages so no two edited the same file. For every page the reviewer read the full inline script and markup, then loaded it from a local copy of the site in headless Chromium and exercised the main flow with realistic input: a domain and an IP, names with diacritics and ampersands, a +44 and a US phone number, BTC bech32 and ETH addresses, coordinates in decimal and DMS, a generated WebM video, a WAV tone, a JPEG with GPS EXIF, a QR PNG, a PDF with Info and XMP metadata, a ZIP with nested and unicode paths, ISC and Windows DHCP leases, valid and invalid VINs. Page errors, console errors and failed requests were captured. Where a third-party API was unreachable from the test environment, the reviewer mocked its documented response shape so the rendering and export code still ran.

Findings were graded critical (tool does not work or produces a dangerous result), high (wrong results or an exploitable injection), medium (fragility: no error state, unpinned dependency, leak) and low (accessibility, polish). Every fix was re-tested the same way, and five site-wide invariants were checked afterwards: no page errors on any of the 140 pages, FAQ text identical to its schema, visible dates equal to dateModified, valid JSON-LD, and ad placeholders unchanged.

Takeaway: reading code finds half the bugs; driving the tool with real input finds the other half.

Twelve tools that did not work at all

These are the ones that embarrass us most, because a visitor could not have told the difference between "broken" and "slow".

  • Stylometry Lab, Data Wrangler, Entity Graph, Steganalysis Lab all did new Worker('…worker.js') on a file that was never deployed. "Load engine" returned a 404 on every visit. The engines are now embedded in the pages, Pyodide is pinned to 0.29.2, and there is a visible loading, retry and failure state. Entity Graph's PageRank also needed SciPy, which Pyodide does not ship by default; it now uses a pure-Python power iteration.
  • Cookie & Tracker Exposer and Repo Security Auditor receive data from a bookmarklet running on the audited site, but the receiver insisted the message come from our own origin. It never did. The tools rendered nothing, ever. Both now validate the payload instead of the origin and escape everything they render.
  • OCR Text Extractor and ZIP↔JSON Converter computed correct results into a panel the CSS hid unless an .active class was set; the scripts only toggled style.display. The OCR page also parsed PaddleOCR's output as an array when the library returns an object, so Paddle mode was always empty.
  • Voice Print Matcher referenced a Hugging Face model id that does not exist. It is now Xenova/wavlm-base-plus-sv, and the WebGPU-to-WASM fallback actually works because the page probes for an adapter before creating the first ONNX session (a failed WebGPU session poisons later WASM sessions in onnxruntime-web).
  • Reverse Image Search had an unclosed <noscript> in its navbar. In any browser with JavaScript on, the entire page body was inside it and invisible.
  • Exposed scanner read its proxy list as an array when the file is an object. Every proxied check (breach data, botnet feeds, archived-file checks) failed silently and was reported as "clean". It now says "not checked" when a check could not run and renormalises the grade.
  • Threat Intel had the same element ids in its IOC scanner and its dashboard pane, so the dashboard's auto-load hijacked the scanner's button and results on every visit.

Takeaway: a tool that renders an empty results area is indistinguishable from a tool that found nothing. Build a visible "could not run" state for every external dependency.

Wrong results that looked right

Harder to spot than dead tools: tools that produce a plausible answer that is wrong.

  • Robtex changed its passive-DNS API to newline-delimited JSON. Three tools (IP Lookup, Domain Recon, Threat Intel) still parsed it as a single JSON document and showed an error or nothing.
  • HackerTarget's free tier returns a plain-text "API count exceeded" message with HTTP 200. Domain Recon rendered that string as if it were DNS data.
  • The Password Generator's "EFF Diceware 7,776-word list" (EFF, 19 July 2016) contained 2,019 words, so the entropy it displayed (12.9 bits per word) was wrong by about two bits per word. Three of its five RNG "engines" were non-cryptographic PRNGs, and the secure one used modulo arithmetic that biases the output. It now uses crypto.getRandomValues with rejection sampling, the full list, and honest entropy maths.
  • The Near-Duplicate Finder's pHash used a median that excluded the DC term (non-standard) and matched flips asymmetrically, so resized copies of the same photo did not always group.
  • Every speech tool resampled audio to 16 kHz by nearest-neighbour, which aliases; they now use OfflineAudioContext.
  • The PDF Metadata tool's "clean" button set the Info keys to empty strings, left custom keys alone, and did not touch the XMP stream. A "cleaned" PDF still carried the author and creator tool.
  • The Steganography tool destroyed hidden bits in any pixel with partial transparency, because canvas stores premultiplied alpha.
  • Weather Chronolocation queried Open-Meteo's forecast endpoint, which holds at most 92 days of history (past_days 0–92), so any older screenshot produced "No data". It now uses the archive endpoint (data from 1940) for older dates.
  • The Timestamp Converter treated 17–19-digit Discord snowflakes (which its own help text invites you to paste) as Unix milliseconds, giving dates millions of years out.
  • Sitemap Historian merged every sitemap file a domain ever had into one timeline, producing fictional diffs, and called any opaque no-cors response, including 404s, "still live".
  • The VIN decoder flagged a check-digit mismatch on every non-North-American VIN and never disambiguated model year (2003 or 2033, always).

Takeaway: a decoder or calculator needs at least one known-answer test you can re-run. We now keep them under version control for every tool.

Security findings

The dominant pattern was third-party API data, or the user's own input, written into innerHTML without escaping. IP Lookup alone had around forty such sinks. CT Monitor rendered certificate SAN hostnames unescaped into table cells and into an onclick attribute, and built a domain tree with plain object keys, so a certificate label of constructor polluted the prototype. Investigation Notes escaped < and & but not ", and interpolated values into value="…" attributes, so an imported notes file could break out. ZIP↔JSON wrote paths from the JSON verbatim, so ../evil.txt produced an archive that extracts outside its folder. Archived URLs from the Wayback CDX were rendered as clickable links even when the scheme was javascript:.

None of these is exotic, and all of them are the natural result of building a UI by string concatenation. Every page now routes text through one escaping helper, blocks non-http(s) schemes before rendering a link, uses rel="noopener" on every target="_blank" (the consent overlay included), and guards CSV exports against formula injection (cells starting with =, +, - or @). The Dork Generator's live Google Suggest, which sent keystrokes to a third party the page never disclosed, is now opt-in.

Takeaway: treat API responses as hostile input. They are someone else's data, served from someone else's server, and they will eventually contain markup.

Reliability and the dependency problem

Several pages loaded libraries from a CDN without a version (leaflet/dist/leaflet.js, pdf-lib/dist/pdf-lib.min.js, jsQR), which means "whatever is latest today". Several more used static ES-module import statements for multi-megabyte model runtimes, so a blocked CDN did not degrade the page, it killed it before the first line of our own code ran. The OCR page downloaded a 10 MB OpenCV build and an ONNX runtime on every visit, including for people who never pressed a button.

Everything is now pinned (jsQR 1.4.0, exifr 7.1.3, Leaflet 1.9.4, Tesseract 5.1.1, onnxruntime-web 1.19.2, Pyodide 0.29.2, JSZip, pdf-lib), loaded lazily on first use, and wrapped so a failed load shows a sentence explaining what did not load and a retry button. Fetches have timeouts, buttons re-enable in finally, rate-limit responses short-circuit with a message instead of a two-minute retry storm, object URLs and canvases are released, and localStorage is wrapped so private browsing and quota errors do not crash the page or silently drop edits.

Takeaway: pin, lazy-load, and give every dependency a failure sentence.

What the audit added along the way

Fixing a tool properly often meant finishing it. Six pages that advertised a search but shipped a static list of links (Address, Geo, Crypto, Video, Transport, Search Engine Directory) now parse the input locally and generate pre-filled pivots: DMS, decimal and map-URL coordinates; BTC, EVM, LTC, DOGE, BCH, TRON, SOL, XRP, XMR and ENS detection; 61 search engines with query templates. The URL Pivot Encyclopedia fills all 145 of its patterns from a typed value. The Evidence Logger has a verifiable SHA-256 hash chain with Verify and Import. The Wikidata Bridge renders every external identifier through Wikidata's own formatter URLs. Cloud IP Identifier covers Oracle, DigitalOcean and Linode as its copy had always claimed. The Repo Auditor checks GitHub Actions workflows, Dockerfiles and dependencies with 26 secret patterns. The DHCP parser reads Windows audit logs, netsh output and dnsmasq syslog. Nearly every tool gained CSV or JSON export, a Copy button, Enter-to-submit, a keyboard-accessible drop zone, an aria-live status region and a ?q= deep link so results can be shared.

What we did not fix

In the interest of the same honesty: the shared proxy worker that four tools use for Wayback CDX queries is currently rate-limited by web.archive.org, so WHOIS History, Wayback Recon, Sitemap Historian and parts of Exposed will show their "rate-limited, try later" state until it clears or we add a second worker. The site-wide note that says requests go through our proxy is inaccurate for most tools, which call upstreams directly; it is being reworded. Several pivot URL shapes (HERE Maps, What3Words, Instant Street View and a few others) could not be verified from the test environment and fall back to the site's own map. The PaddleOCR path could not be run end-to-end in testing because of the OpenCV download size; the fix is based on the library's source. WHOIS History is really a Wayback contact scraper and will be renamed. And the per-state ALPR camera table, which is content rather than a tool, was out of scope.

The checklist we now run

If you maintain browser-side tools, this is the list that would have caught almost everything above. It takes about twenty minutes per tool.

  1. Load the page with the network panel open. Does every script, stylesheet, worker and model URL return 200? Is each pinned to a version?
  2. Block the CDN (or go offline) and reload. Does the page tell you what failed, or does it hang?
  3. Run the main flow with a known input whose correct output you already know. Compare, don't eyeball.
  4. Run it with hostile input: <img src=x onerror=alert(1)> as a name, a javascript: URL in any field that renders links, ../ in any path, =1+1 in anything that exports CSV.
  5. Make the upstream API fail (mock a 429, a 500, an HTML error page with status 200, an empty body). Does the tool say "could not check", or does it say "clean"?
  6. Click every Copy, Export and Download button and open what comes out.
  7. Press Tab through the page. Can you reach and operate every control, including drop zones, without a mouse?
  8. Resize to 375 px wide. Anything overflow?
  9. Run the tool five times in a row without reloading. Does memory climb? Do stale results leak into the next run?
  10. Check the help text and FAQ against what the tool now does. Ours described a 30-technique forensics engine that no longer existed.

Takeaway: schedule it. We now re-run this list quarterly, and the freshness tracker shows when each page is due.

Findings per tool

Counts are the findings recorded in the audit reports, by severity. "Was non-functional" marks tools that returned nothing before the fix. The eight AI-dork prompt pages share one template and are counted once.

ToolCriticalHighMediumLowTotal
Wayback & CommonCrawl Recon31318
OCR Text Extractor was non-functional2316
Audio & Video Transcriber224210
Voice Print Matcher was non-functional22217
MAC Address Lookup21216
Stylometry Lab was non-functional21216
ZIP↔JSON Converter was non-functional1315
Video Person Tracker124411
Face Finder12328
Data Wrangler was non-functional12216
Entity Graph was non-functional12238
Exposed — Security Scanner was non-functional12216
Facebook OSINT1214
Repo Security Auditor was non-functional12115
PDF Metadata Tool1214
Cookie & Tracker Exposer was non-functional11215
Reverse Image Search was non-functional11114
Threat Intel was non-functional11114
Steganalysis Lab was non-functional112
Address OSINT112
Crypto OSINT112
Geolocation OSINT112
IP Lookup & Triage35210
Domain Recon Dashboard325
Investigation Notes314
Password Generator2529
Cloud IP Identifier2316
Connection Fingerprint2327
EXIF Viewer2327
Sitemap Historian2316
Tech Stack Detector2338
Career Page Intelligence2215
Ghost Finder (people search)224
QR Code Decoder2215
Weather Chronolocation2215
DHCP Lease Parser2114
Dork Generator2114
CT Monitor1517
WHOIS History (Wayback contact scraper)1427
Evidence Logger1315
Email OSINT123
News Search123
Social Media Search123
Steganography Tool1214
AI Voice Detection1113
Document OSINT1113
Search Engine Directory112
Phone OSINT1113
Timestamp Converter1113
Transport OSINT1113
Video OSINT1113
VIN Decoder1113
Vital Records1113
Wikidata Bridge1113
Near-Duplicate Image Finder415
RDAP Lookup314
Stylometry Analyzer347
Business Records213
Domain OSINT224
ICAO Hex Decoder213
Report Builder213
URL Pivot Encyclopedia213
Username Search213
AI Investigation Planner112
AI Dorks (8 prompt pages)0
Threat Dashboard0

Frequently Asked Questions

How many of the tools were actually broken before the audit?

Twelve of 71 interactive pages did not work at all: Stylometry Lab, Data Wrangler, Entity Graph and Steganalysis Lab (missing worker files), Cookie & Tracker Exposer and Repo Security Auditor (receiver rejected every message), OCR Text Extractor and ZIP↔JSON Converter (hidden results panel), Voice Print Matcher (non-existent model id), Reverse Image Search (unclosed noscript hid the page), the Exposed scanner (proxy list misread, so checks silently reported clean) and Threat Intel (duplicate element ids). Many others returned wrong or partial results.

Were any of the security findings exploitable by a visitor?

The injection findings were cross-site scripting through data the tool rendered: third-party API responses, archived URLs, certificate names, file names inside a ZIP, or imported JSON. Exploiting them required controlling that data (for example, registering a certificate with a hostile SAN, or sending someone a crafted notes export). We have no evidence any were used. All rendering now goes through an escaping helper and non-http(s) link schemes are blocked.

Why did nobody notice for so long?

Because the failures were silent. A tool whose engine failed to load, or whose receiver rejected every message, showed an empty results area that looked exactly like "nothing found". There was no error message, no monitoring, and no known-answer test to re-run. Each tool now has a visible failure state for every external dependency, and we keep a test input with a known correct output for every decoder and calculator.

How was the audit performed?

Eight reviewers took disjoint groups of pages. Each read the full source, then loaded the page in headless Chromium from a local copy of the site and exercised the main flow with generated inputs (test video, audio, images with EXIF, QR codes, PDFs, ZIPs, DHCP leases, VINs, addresses). Unreachable third-party APIs were mocked with their documented response shapes. Every fix was re-tested the same way and the whole site was checked for page errors, FAQ-schema parity, date consistency and valid JSON-LD.

Can I see the full findings?

The changelog entry for 6 October 2026 summarises them, and the table at the end of this article gives counts per tool. Detailed per-page notes, including what was recommended but not changed and how each page was tested, are kept with the site's source and are available on request.