{"slug": "ai-blinder", "title": "AI Blinder", "summary": "Blinder, a new open-source local HTTPS reverse proxy written in Go, strips target identity — domains, brands, organization names, emails and IP addresses — from HTTP and WebSocket traffic so AI-assisted security scanners evaluate application behavior instead of forming opinions based on who the target is. The tool presents the target to the downstream AI as an anonymous locally hosted application at https://127.0.0.1:8099 while preserving behavioral signals such as error messages, reflected input, status codes and security controls, with options including --preserve-content for identity-only scrubbing and --extra-origin for multi-origin routing. The project's author frames Blinder as an early position that testing should be separated from context, and invites better implementations and contributions.", "body_md": "**A local reverse proxy for content-blind security scanning.**\n\n  Go · Local HTTPS · HTTP & WebSocket · Tor / SOCKS5\n\n[The reasoning](#the-reasoning)   /  \n  [How it works](#how-blinder-works)   /  \n  [Quick start](#quick-start)   /  \n  [Certificates](#certificates)   /  \n  [Tor](#tor)   /  \n  [CAPTCHA](#captcha)   /  \n  [Evidence](#evidence)   /  \n  [Development](#development)\n\nSecurity testing is a behavioral discipline. What an application does -- how it handles input, what controls it enforces, what it reflects back, how it fails -- is what matters. Identity should be irrelevant to that analysis.\n\nAI-assisted security tools don't work that way. They see the target -- its domain, its brand, its organization -- and they form opinions. They soften findings for well-known services. They refuse to probe based on *who* the target is. They decline to test paths they associate with a particular vendor. The AI is making decisions that belong to the operator, and it's making them based on context rather than behavior.\n\nThis is the wrong axis. The operator authorizes the scope. The tool evaluates behavior. Those are different responsibilities and they shouldn't collapse into one. But today, every AI-assisted tool has the target's full identity wired into every decision it makes -- what to test, how hard to push, whether to report.\n\nBlinder is a starting point: a practical tool, but also a position that testing should be separated from context. This is an early attempt to get the idea out there. If the approach resonates, we'd welcome better implementations, contributions, or just the conversation about where the line should be.\n\nStripping identity doesn't mean stripping content. That's where most naive approaches break. Vulnerabilities are observable as changes in response content -- error messages, reflected input, data the session shouldn't reach, computed results that reveal server-side evaluation. If a proxy stripped this content, it would hide the evidence the tester is looking for.\n\nThe requirement is surgical: remove *identity* while preserving *behavioral signals*. A page that belongs to no one but behaves exactly as the original does -- including when it behaves badly.\n\nBlinder is a local HTTPS reverse proxy that sits between the AI scanner (or browser) and the target. It rewrites identity -- domains, brands, organization names, emails, IP addresses -- while preserving the application's functional behavior: its errors, its reflections, its security controls, its status codes, its content structure.\n\nTo the downstream AI, the target is an anonymous locally hosted application at `https://127.0.0.1:8099`. No brand to recognize. No domain to form an opinion about. The AI tests what the application *does*, not who it *is*.\n\nThis is content-blind scanning: the operator controls who the target is; the AI focuses on what it does.\n\nReplacement content is part of correctness: neutral filler for display, reversible values for application data, and preserved diagnostics and control behavior. Generated removal notices don't belong in pages.\n\n| **Content scrubbing** | Display text replaced with neutral prose filler; interactive elements (buttons, labels, form controls) and diagnostic content (error messages, stack traces, reflected markup) preserved. Identity tokens, domain references and cookie values rewritten across HTTP bodies, headers and WebSocket text. `--preserve-content` keeps original display text for identity-only scrubbing. | \n| **Resource integrity** | Original SRI verified per reference and recomputed for rewritten resources, with corresponding CSP hashes translated. Versioned references bind the served bytes; external resource integrity is preserved. | \n| **Response cache** | Separate upstream/downstream cache validators. 304 revalidation merges security-policy headers. Vary-aware eviction. | \n| **Session handling** | Reversible cookie names with per-value scrubbing. Multi-origin routing via `--extra-origin` with deterministic alias hostnames, Host-header routing and CORS origin translation. | \n| **CAPTCHA relay** | Operator-facing challenge queue and separate provider origins. Tor-routed resources keep provider cookies, CSP and CORS separate from the target and operator. | \n| **Private routing** | Upstream HTTP and WebSocket through Tor SOCKS5 with remote hostname resolution. Tor failures are hard errors, never silent fallbacks. | \n| **Local HTTPS** | Local CA with 90-day lifetime and automatic renewal; session leaf certificates are signed on the fly. Trust the CA once -- adding origins or changing aliases never requires re-trusting. Ephemeral mode available. | \n| **Evidence** | Pre-scrub HAR with journal-based persistence, request manifest with per-request scrub/leak counts, domain mappings and scrub report. [Paired response comparisons](https://github.com/Splinters-io/blinder/blob/main/docs/response-deltas.md) check byte-size fidelity and whether content/status changes survive masking. Signal-preservation checks record verified behavior and remaining defects. | \n\nSee [supported behavior and delivery gates](https://github.com/Splinters-io/blinder/blob/main/docs/capabilities.md) for implementation status and known limitations.\n\nBuild with **Go 1.26+**. The binary has no external runtime dependency.\n\n```\ngit clone https://github.com/Splinters-io/blinder.git\ncd blinder\nmake build\n./blinder --preflight\n```\n\nFollow the OS-specific certificate advice, then start a session:\n\n```\ncapture_dir=$(mktemp -d)\n./blinder --target https://your-authorized-target.example \\\n  --identity YourOrganisation \\\n  --har \"$capture_dir/session.har\" \\\n  --output \"$capture_dir/output\"\n```\n\nPoint your browser or scanner at **`https://127.0.0.1:8099`**. Add multiple identity tokens with repeated `--identity` flags. Stop with **Ctrl-C** to save the session evidence.\n\nUse `--config` (`-c`) to load defaults from a YAML file. CLI flags override the file.\n\n```\n# blinder.yaml\nlisten: \"127.0.0.1:9443\"\ntarget: \"https://example.com\"\nalias: \"target-001.local\"\nidentity:\n  - \"ExampleCorp\"\n  - \"example.com\"\noutput: \"/tmp/blinder-output\"\ncaptcha_config: \"captcha.yaml\"\nno_verify_tls: true\ntor:\n  enabled: false\n  addr: \"127.0.0.1:9050\"\nhar:\n  path: \"/tmp/session.har\"\n  max_body: 10485760\n./blinder -c blinder.yaml\n# override the listen port from the file:\n./blinder -c blinder.yaml --listen 127.0.0.1:7777\n```\n\nBlinder generates a local CA (Certificate Authority) on first run and uses it to sign session-specific leaf certificates. Trust the CA once; all current and future endpoints -- including extra origins added later -- are automatically trusted without re-running setup.\n\n| Platform | Setup | \n|---|---|\n| **macOS** | Run the printed `--trust-cert` command, review the CA fingerprint and approve user Keychain trust. One-time setup. | \n| **Ubuntu / Linux** | Use the printed `curl --cacert` command or install the CA certificate in your browser/scanner's trust store. | \n\nUse `--cert-dir DIR` for a chosen CA store. The CA persists for 90 days with automatic renewal; `--ephemeral-cert` generates a temporary self-signed leaf with no CA. Preflight exit **2** means platform trust needs setup; a client using its own CA file can still connect successfully.\n\nAdding extra origins, changing the alias or reconfiguring CAPTCHA providers generates a new leaf certificate signed by the same CA -- no re-trusting required. Preflight reports trust for the listen host, primary alias, configured extra-origin aliases and, when CAPTCHA is configured, the operator and a concrete challenge hostname. With `--tor`, preflight also includes the configured provider aliases.\n\nThe CA store also holds a private `version-signing.key` for resource-reference ownership, independent of certificate renewal. Keep it across restarts so expired references remain recognisable. `--ephemeral-cert` keeps TLS temporary; resource-reference ownership still persists in the default store.\n\n[Certificate setup, OS guidance and renewal](https://github.com/Splinters-io/blinder/blob/main/docs/testing.md#local-certificate-trust)\n\nStart your Tor service and wait for bootstrap, then select its SOCKS endpoint:\n\n```\n./blinder --target http://your-service.onion \\\n  --tor --tor-addr 127.0.0.1:9050 \\\n  --identity YourOrganisation\n```\n\nClearnet targets can use the same route. Target TLS verification stays enabled. Tor failures return an error without falling back to a direct target connection.\n\n[Tor setup and live acceptance checklist](https://github.com/Splinters-io/blinder/blob/main/docs/testing.md#live-tor-uat)\n\nWhen the target returns a CAPTCHA challenge, Blinder queues it for the operator. Configure providers via `--captcha-config`:\n\n```\nversion: 1\ncaptcha:\n  providers:\n    - hcaptcha\n```\n\nOpen the browser login URL printed at startup: `https://blinder-operator.localhost:<port>/__blinder/captcha/login?token=…`. This separate, local-only origin holds the operator session. Raw challenge content runs at its own `<challenge-id>.blinder-challenge.localhost` origin through an expiring view capability; the operator cookie stays on the operator origin. Bearer authentication remains available for API clients. Certificate planning includes the challenge wildcard; verify the selected browser's trust for the operator, challenge and provider hostnames. See the [operator and certificate guide](https://github.com/Splinters-io/blinder/blob/main/docs/testing.md#local-certificate-trust).\n\nWith `--tor`, each configured `route-with-target` provider origin gets its own `https://captcha-<hash>.localhost:<port>` address and uses the target's SOCKS transport. Static HTML references, base URLs and refresh navigation use these routes. A bounded helper also routes dynamic fetch, XHR and URL setters in provider documents without CSP; it leaves provider script bytes and integrity metadata unchanged. Documents with enforcing or report-only policies receive no helper. Provider errors and actual CORS decisions remain visible, and cookies stay under browser control. The old `/__blinder/captcha/res` endpoint returns 404 on target and operator origins.\n\nBuilt-in profiles cover hCaptcha, reCAPTCHA and Turnstile. Custom providers use explicit `resource_origins`, optional `resource_url_regex`, `opaque_fields` and `submissions`; every relayed request is checked against that scope. Direct mode leaves provider references intact, and `tor_policy: direct` remains an explicit Tor exception.\n\nChrome completed the synthetic operator flow: all 13 provider requests used the relay, and the original POST resumed with its exact solution token, session and refreshed CSRF. A separate cross-site Lax-cookie case preserved the direct provider's HTTP 400 denial. This is bounded local acceptance: CSP-restricted challenges, other dynamic loading APIs, actual alias trust, real-provider human completion and live Tor/onion acceptance remain open. The [repeatable browser checks](https://github.com/Splinters-io/blinder/blob/main/docs/testing.md#captcha-browser-acceptance) record those gates separately.\n\nhcaptcha and reCAPTCHA reject `localhost` and `127.0.0.1` as hostnames. To test locally with those providers, add a hosts entry and use that hostname as the `--target`:\n\n```\n# /etc/hosts\n127.0.0.1 uat.blinder.test\nblinder --target http://uat.blinder.test:9999 ...\n```\n\nTurnstile test keys work on localhost without a hosts entry.\n\n| Provider | Test sitekey | Test secret | \n|---|---|---|\n| hcaptcha | `10000000-ffff-ffff-ffff-000000000001` | `0x0000000000000000000000000000000000000000` | \n| Turnstile | `1x00000000000000000000AA` | `1x0000000000000000000000000000000AA` | \n\nhcaptcha test keys always pass without a visual challenge. Turnstile test key `1x...AA` always passes; `3x00000000000000000000FF` forces an interactive challenge.\n\nKeep original captures on the operator's side; they contain real target data.\n\n**Security note:** HAR files contain the complete pre-scrub HTTP exchange, including `Authorization`, `Cookie` and `Set-Cookie` headers. They are written with owner-only permissions (0600), but anyone with access to the file has every credential that transited the proxy during that session. Treat HAR files as secrets: do not commit them to version control, share them without redaction, or store them on shared filesystems.\n\n| Output | Contents | \n|---|---|\n| `--har FILE` | Original HTTP requests and responses before scrubbing. Journal-based persistence with periodic flushes. | \n| `blinder-manifest.json` | HTTP outcomes, per-request identity/domain replacement counts and leak counts. | \n| `blinder-dealias.json` | Alias-to-original domain mapping. | \n| `blinder-scrub-report.json` | Recorded identity/domain matches and aggregate counts. | \n\nJSON reports are saved under `--output DIR` with owner-only file permissions. `--har-max-body` bounds each captured request/response body, with truncation recorded in the HAR. Shutdown attempts both HAR and report output even if one fails.\n\n[Capture behavior and artifact format](https://github.com/Splinters-io/blinder/blob/main/docs/capabilities.md#evidence)\n\n```\nmake test              # 768 package tests with race detection\nmake lint              # Go vet\nmake test-functional   # 35 functional scenarios: CLI, TLS, sessions,\n                       # evidence, WebSocket, SOCKS5 and CAPTCHA\n```\n\nAll tests pass with `-race`. Browser-level acceptance tests are opt-in via `BLINDER_REVIEW_BROWSER=1`.\n\n[Testing and UAT guide](https://github.com/Splinters-io/blinder/blob/main/docs/testing.md) · [Delivery gates](https://github.com/Splinters-io/blinder/blob/main/docs/capabilities.md#remaining-delivery-gates) · [Technical specification](https://github.com/Splinters-io/blinder/blob/main/SPEC.md)\n\n<sub>LESS IDENTITY. MORE SIGNAL.</sub>\n\n[Project artwork](https://github.com/Splinters-io/blinder/blob/main/docs/brand.md)", "url": "https://wpnews.pro/news/ai-blinder", "canonical_source": "https://github.com/Splinters-io/blinder", "published_at": "2026-09-30 18:36:33+00:00", "updated_at": "2026-09-30 18:49:04.004032+00:00", "lang": "en", "topics": ["ai-tools", "ai-safety", "developer-tools", "artificial-intelligence"], "entities": ["Blinder", "Go", "Tor", "SOCKS5"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/ai-blinder", "markdown": "https://wpnews.pro/news/ai-blinder.md", "text": "https://wpnews.pro/news/ai-blinder.txt", "jsonld": "https://wpnews.pro/news/ai-blinder.jsonld"}}