feat: browser-driven Hover auth (defeat Imperva) via GoCodeAlone/rod — v0.5.0#30
Merged
Merged
Conversation
…ABP) Replace cold-HTTP login with a real-Chrome (go-rod) session driver that runs Imperva's JS sensor + mints clearance; full-browser flow (TLS/JS/cookie consistency); system/cached/container Chrome; stealth. Login-only optimization deferred to an empirical scope test with the verified test account. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
go-rod probe (ProbeLiveBrowserAuth) + opt-in live test. Launches Chrome, strips navigator.webdriver, waits for Imperva clearance cookies, submits signin (incl. 2FA path) via in-browser fetch, then probes whether a plain Go http.Client can reuse the clearance for /api/domains (login-only optimization signal). Live test skips unless HOVER_LIVE_TEST=1; never logs secrets. go-rod is the driver picked by the both-drivers Imperva spike. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Records that both playwright-go and go-rod cleared Imperva headless in the scratch spike; go-rod picked for pure-Go runtime. De-risks Assumption #1. Full authenticated login still gated on test-account verification. No manifest change; no scope unlock required. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ged) No maintained Go-native Imperva-bypass lib; SOTA stealth tools all Python/Node (wrong language per ADR 0001). New 2026 signals (JA4 + UA-CH consistency) reinforce full-browser default. CDP-protocol fingerprinting flagged as most likely future-break vector for any CDP driver incl go-rod. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Live gate caught a panic: go-rod KeepUserDataDir() only works on a managed launcher; on a local launcher it panics (mustManaged). And Cleanup() deletes UserDataDir. Drop KeepUserDataDir(); use Kill() in cleanup so the persistent profile (and its Imperva clearance/cookies) survives across calls. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
go-rod reached auth.json (need_2fa), not the Imperva 401 → Assumption #1 validated. Records Hover's email-default 2FA, new-device challenge, and the CI auth model (TOTP secret and/or persistent trusted profile). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bumps go directive 1.26.0->1.26.3 (fixes stdlib html/template/net/net/http vulns GO-2026-4982/4980/4971/4918) and x/net 0.54.0->0.55.0 (GO-2026-5026). gopls modernize: maps.Copy for manual copy loops. govulncheck 7->2 affecting (remaining 2 are docker/docker via workflow SDK, no upstream fix). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Records the maintained-fork decision (amends ADR 0001's driver source) and backports the CI auth model + scope amendment (dep source change + prod live-login proof via gh workflow on the self-hosted runner). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Repoints the go-rod CDP driver to the maintained fork github.com/GoCodeAlone/rod v0.116.3 (ADR 0002). Build/vet/unit green; govulncheck adds no new vulns (only the pre-existing docker/docker SDK transitive remains, no upstream fix). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…0 domains) CI probe on self-hosted runner vs production: go-rod fork clears Imperva, TOTP completes new-device 2FA, 30 domains read, go_http_reuse_viable=true. Adopts login-only transport (browser login -> Go HTTP for API) per the design's conditional, with full-browser fallback for the TLS-fingerprint risk and write paths. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Introduce executionBackend interface routing Login/ListDomains/etc. to either httpBackend (injected http.Client — tests) or browserBackend (nil http.Client — production Chrome path). NewClientWithOptions parses explicit BrowserOptions; NewClient preserves backward-compat signature. Provider Initialize parses browser_path/download/headless/profile_dir config keys with HOVER_BROWSER_* env fallbacks via parseBrowserConfig. browserBackend live ops return ErrBrowserBackendUnavailable (Task 3). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ies to HTTP reads Task 3: implement browserBackend.Login (Chrome launch via launchBrowserWithHandles, navigator.webdriver strip, UA/AcceptLanguage stealth, waitForClearanceCookies, submitBrowserSignin with TOTP, cookie handoff to c.http.Jar), typed errors (ErrBotChallenge / ErrChromeUnavailable / ErrEmail2FARequired), read delegation (ListDomains/GetDomain/ListRecords/GetDomainDelegation → HTTP backend after login), writes still return ErrBrowserBackendUnavailable (Task 4). Ten new tests in browser_backend_test.go drive real go-rod against local httptest servers. Add .hover-browser-profile/ to .gitignore. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…path) Implement Task 4: CreateRecord, UpdateRecord, DeleteRecord, and SetNameservers on browserBackend now execute in-page via Chrome fetch (credentials:'include') so requests carry the live Imperva clearance and Chrome TLS fingerprint. Generalise browserSigninFetch into reusable browserFetchJSON/browserFetchWithHeaders helpers (probe + writes share one in-page fetch path). SetNameservers extracts CSRF from the control_panel page DOM in-browser then PUTs with X-CSRF-Token. Guards b.browser == nil with a clear "not initialised" error when Login was never run. All endpoints/payloads/typed-errors preserved from HTTP impls. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rewrite README to document the real Chrome/go-rod auth architecture (Imperva ABP bypass, hybrid browser-login + HTTP-read + in-browser-write model, Chrome acquisition, BrowserOptions config keys + env aliases, TOTP/email-2FA requirements, browser profile dir, typed errors). Remove stale CSRF form-login description. Bump both plugin.json manifests from 0.0.0 to 0.5.0 (behavioral minor for the browser-auth backend). Update iacserver_live_test.go: gate on HOVER_LIVE_TEST=1, source browser opts via BrowserOptionsFromEnv + config keys from env, exercise the full provider Initialize → EnumerateAll → Import → Status path (typed gRPC server surface), skip cleanly when HOVER_LIVE_TEST is unset. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Task 6: security review (PASS — no Critical/High; one tracked UA-derivation resilience follow-up). Fixes Login mislabeling a cookie-read error as ErrBotChallenge (only a clearance timeout is a challenge now). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Introduces a browser-driven authentication path for Hover (Imperva ABP) using github.com/GoCodeAlone/rod, while preserving the existing provider/IaC public contract by routing operations through an internal backend seam (browser for login + writes; Go http.Client cookie reuse for reads).
Changes:
- Added
executionBackendseam and a Chrome/rod-based backend that mints Imperva clearance cookies, completes TOTP 2FA, and reuses cookies for HTTP reads. - Added browser runtime configuration (config keys + env aliases), typed errors, and extensive local httptest-driven rod tests plus opt-in live probes.
- Updated docs/manifests for the new auth architecture and bumped release version to
v0.5.0.
Reviewed changes
Copilot reviewed 27 out of 29 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | Documents the new Chrome-based auth, hybrid read/write behavior, config/env options, and limitations. |
| plugin.json | Bumps plugin manifest version to 0.5.0. |
| cmd/workflow-plugin-hover/plugin.json | Bumps embedded plugin manifest version to 0.5.0. |
| .gitignore | Ignores .hover-browser-profile/ local profile state. |
| go.mod | Adds github.com/GoCodeAlone/rod and bumps Go/toolchain-related dependencies. |
| go.sum | Records sums for rod and updated transitive deps. |
| pkg/hoverclient/options.go | Adds ClientOptions for passing runtime options (browser config). |
| pkg/hoverclient/backend.go | Introduces executionBackend interface + httpBackend delegator. |
| pkg/hoverclient/client.go | Routes login and API methods via backend; adds NewClientWithOptions; keeps signatures stable. |
| pkg/hoverclient/client_test.go | Adds tests asserting backend selection behavior and options preservation. |
| pkg/hoverclient/browser_options.go | Defines BrowserOptions, defaults, env parsing, and profile-dir defaulting. |
| pkg/hoverclient/browser_probe.go | Implements a live browser auth probe + cookie reuse viability probe. |
| pkg/hoverclient/browser_live_test.go | Adds opt-in live test gated by HOVER_LIVE_TEST=1. |
| pkg/hoverclient/browser_backend.go | Implements rod-driven login, typed errors, cookie handoff, and in-browser write operations. |
| pkg/hoverclient/browser_backend_test.go | Adds local httptest + rod unit tests for login, error classification, cookie handoff, read delegation, close behavior. |
| pkg/hoverclient/browser_backend_write_test.go | Adds local httptest + rod unit tests for in-browser DNS write operations and CSRF handling. |
| internal/provider.go | Parses provider browser config and passes it into hoverclient.NewClientWithOptions. |
| internal/provider_test.go | Adds tests covering provider browser config parsing and env aliases. |
| internal/iacserver.go | Replaces manual map copies with maps.Copy. |
| internal/iacserver_live_test.go | Expands live IaC tests to cover Initialize→EnumerateAll→Import→Status using browser backend. |
| docs/plans/2026-05-30-headless-browser-auth.md | Captures the implementation plan for the browser auth work. |
| docs/plans/2026-05-30-headless-browser-auth.md.scope-lock | Records scope-lock hash for plan verification. |
| docs/plans/2026-05-30-headless-browser-auth.plan-review-1.md | Plan review notes and corrections. |
| docs/plans/2026-05-30-headless-browser-auth.alignment-report-1.md | Design↔plan alignment report. |
| docs/plans/2026-05-30-headless-browser-auth-design.md | Design doc for the Imperva/Chrome approach, risks, and validation. |
| docs/plans/2026-05-30-headless-browser-auth-design.adversarial-review-1.md | Adversarial design review notes. |
| docs/plans/2026-05-30-headless-browser-auth.security-review.md | Security review report for the headless-browser auth approach. |
| decisions/0001-real-browser-auth-for-imperva.md | ADR documenting the decision to use a real browser to defeat Imperva ABP. |
| decisions/0002-fork-go-rod-for-maintenance-and-dep-control.md | ADR documenting the fork to GoCodeAlone/rod for maintenance/dep control. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+56
to
+66
| type browserBackend struct { | ||
| opts BrowserOptions | ||
|
|
||
| // overrideHost replaces hoverHost for local tests. Empty means production | ||
| // (uses hoverHost). Never set in production code. | ||
| overrideHost string | ||
|
|
||
| // Live handles, set by Login and torn down by Close. | ||
| browser *rod.Browser | ||
| launcher *rodlauncher.Launcher | ||
| } |
Comment on lines
+68
to
+70
| func newBrowserBackend(opts BrowserOptions) *browserBackend { | ||
| return &browserBackend{opts: opts} | ||
| } |
Comment on lines
+92
to
+104
| func (b *browserBackend) Login(ctx context.Context, c *Client) error { | ||
| c.mu.Lock() | ||
| alreadyFresh := !c.loggedAt.IsZero() && time.Since(c.loggedAt) < sessionStaleAfter | ||
| c.mu.Unlock() | ||
| if alreadyFresh { | ||
| return nil | ||
| } | ||
|
|
||
| if b.opts.Timeout > 0 { | ||
| var cancel context.CancelFunc | ||
| ctx, cancel = context.WithTimeout(ctx, b.opts.Timeout) | ||
| defer cancel() | ||
| } |
Comment on lines
+111
to
+118
| } else { | ||
| // Explicit path provided: validate it exists (unless Download would | ||
| // handle it, but an explicit path means the operator chose a specific | ||
| // binary — honor it literally). | ||
| if _, err := os.Stat(b.opts.Path); err != nil && !b.opts.Download { | ||
| return fmt.Errorf("%w: %s: %v", ErrChromeUnavailable, b.opts.Path, err) | ||
| } | ||
| } |
Comment on lines
+178
to
+180
| Browser unit tests in `pkg/hoverclient` launch real Chrome locally and may take | ||
| ~20 s. They skip automatically when no Chrome binary is available and | ||
| `HOVER_BROWSER_DOWNLOAD` is not set. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replaces Hover's broken cold-HTTP signin (always 401 behind Imperva ABP) with a real-Chrome auth path that runs Imperva's JS sensor, mints clearance, completes TOTP 2FA, and operates the account — validated against production Hover.
Proof (production, self-hosted CI)
go_http_reuse_viable=true domains=30 clearance_cookies=[__uzma __uzmb __uzme __uzmc __uzmd __ssds __ssuzjsr0]— go-rod fork clears Imperva, TOTP completes new-device 2FA, 30 real domains read. (gocodealone-dns run 26784365604.)Architecture (hybrid)
github.com/GoCodeAlone/rod(our maintained go-rod fork, ADR 0002): mint Imperva clearance + TOTP.http.Client(Imperva clears the session, not per-request).fetch+ CSRF) for TLS-fingerprint consistency.executionBackendseam —hoverclient.Client/ gRPC / IaC provider contract unchanged. HTTP backend retained for injected-client unit tests.Notable
github.com/GoCodeAlone/rodfork (renamed module, Go 1.26.3, govulncheck/Dependabot/CodeQL clean) — upstream go-rod is stale since 2024.browser_path/browser_download/browser_headless/browser_profile_dir(+HOVER_BROWSER_*env). Profile dir defaults under$XDG_STATE_HOME, gitignored.ErrBotChallenge,ErrChromeUnavailable,ErrEmail2FARequired(email-2FA accounts can't headless-login — use TOTP or a pre-trusted profile).docs/plans/2026-05-30-headless-browser-auth.security-review.md(PASS).Rollback
Revert the plugin version pin to v0.4.2 (note: v0.4.2 cannot auth live Imperva — rollback = Hover automation disabled, not "old working behavior"). Change is additive behind the unchanged interface.
🤖 Generated with Claude Code