Conversation
…ies and fix delimiter in CategoryFor
… and category validation
…alloc in filewriter, fix session doc
This binary is tracked on main and was incidentally deleted earlier on this branch. Restoring it keeps the 13.4MB binary out of this PR's diff. Removing the tracked binary from main should be done in a separate PR.
Sayan-
left a comment
There was a problem hiding this comment.
Overall looks good! Reading through #216 was super helpful to ground the motivation of some of these changes.
I do think the pending bot comments should be addressed.
From a scalability perspective a few properties I noticed:
- Publish is synchronous and serialized. Every handler blocks on
CaptureSession.Publish, which holds a session-wide mutex and writes to disk inline. - Per-response goroutine fanout for body fetches.
handleResponseReceivedspawns one unbounded goroutine per textual response to callNetwork.getResponseBody. - Screenshots are inline base64 PNGs carried through the same publish path.
I think the units have reasonable coverage. As we get closer to shipping, I'd love to see a consistent stress test against a real page so we can understand latency, memory, and perf implications.
092a265 to
7550bc1
Compare
…116/cdp-foundation
| _ = m.injectScript(ctx, p.SessionID) | ||
| } | ||
| }) | ||
| } |
There was a problem hiding this comment.
Injected script never runs on already-loaded pages
Medium Severity
handleAttachedToTarget calls injectScript, which only uses Page.addScriptToEvaluateOnNewDocument. This registers interaction.js for future navigations but never evaluates it on the current document. Pages already loaded when the monitor attaches (via attachExistingTargets or after reconnect) won't have click, keydown, or scroll-settled tracking until their next navigation. A Runtime.evaluate call with the same script source is needed alongside the addScriptToEvaluateOnNewDocument registration to cover the current page.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit bf4b04c. Configure here.
| } | ||
| // name/id/aria-label heuristic as fallback, covers custom controls that use ARIA. | ||
| var name = (el.name || el.id || (el.getAttribute && el.getAttribute('aria-label')) || ''); | ||
| return SENSITIVE_NAME_RE.test(name); |
There was a problem hiding this comment.
Sensitive field regex only checks first non-empty attribute
Medium Severity
The isSensitiveInput function uses a || chain (el.name || el.id || ... aria-label ...) that short-circuits on the first truthy value, so only one attribute is ever tested against SENSITIVE_NAME_RE. If el.name is non-empty but non-sensitive (e.g. "field1"), a sensitive el.id like "password_input" or aria-label is never checked. Keystrokes in such fields are captured with the actual e.key value. Notably, shouldSuppressClickText already handles this correctly by testing id and aria-label independently — the same approach is needed here.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 4feef7e. Configure here.
| _, err := m.send(ctx, "Page.addScriptToEvaluateOnNewDocument", map[string]any{ | ||
| "source": injectedJS, | ||
| }, sessionID) | ||
| return err |
There was a problem hiding this comment.
Interaction script not injected into current document
Medium Severity
injectScript only calls Page.addScriptToEvaluateOnNewDocument, which registers the interaction-tracking JS for future navigations. The already-loaded document in an attached target never receives the script. When the monitor attaches to existing pages (via attachExistingTargets at startup or after reconnect), clicks, keydowns, and scroll events on those pages won't be captured until the user navigates away. A companion Runtime.evaluate call is needed to inject into the current document.
Reviewed by Cursor Bugbot for commit 8e94162. Configure here.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 6 total unresolved issues (including 5 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 5465e59. Configure here.
| key: e.key, | ||
| selector: sel(t), tag: t.tagName || '' | ||
| })); | ||
| }, true); |
There was a problem hiding this comment.
Sensitive input detection bypassed by shadow DOM retargeting
Medium Severity
The keydown handler uses e.target to check isSensitiveInput, but e.target is retargeted across shadow DOM boundaries. When a <input type="password"> lives inside a web component's shadow DOM (common with Material UI, Lit, Shoelace, etc.), e.target at the document level resolves to the shadow host custom element — not the inner password input. Since the shadow host typically isn't an INPUT/TEXTAREA, isEditable returns false and isSensitiveInput returns false, allowing the actual e.key character to be captured. Using e.composedPath()[0] instead of e.target would resolve this, as it returns the real originating element even across shadow boundaries.
Reviewed by Cursor Bugbot for commit 5465e59. Configure here.


Introduces the foundational layer of the CDP monitor as a standalone reviewablechunk. No Monitor struct wiring, just the primitives that everything else builds on.
types.go: CDP wire format (cdpMessage), all event type constants, internal state structs (networkReqState, targetInfo, CDP param shapes).
util.go: Console arg extraction, MIME allow-list (isCapturedMIME), resource type filter (isTextualResource), per-MIME body size caps (bodyCapFor), UTF-8-safe body truncation (truncateBody).
computed.go: State machine for the three derived events: network_idle (500ms debounce after all requests finish), layout_settled (1s after page_load with no layout shifts), navigation_settled (fires once all three flags converge). Timer invalidation via navSeq prevents stale AfterFunc callbacks from publishing for a previous navigation.
domains.go: isPageLikeTarget predicate (pages and iframes get Page.* / PerformanceTimeline.*; workers don't), bindingName constant, interaction.js embed.
interaction.js: Injected script tracking clicks, keydowns, and scroll-settled events via the __kernelEvent CDP binding.
Note
Medium Risk
Adds a new long-lived CDP monitoring subsystem that captures network/console data (including headers/bodies) and manages reconnect/timers/concurrency, so bugs could impact event fidelity, memory use, or sensitive-data exposure.
Overview
Adds a new
cdpmonitorpackage that maintains a CDP WebSocket connection with auto-attach to targets, dispatches Runtime/Network/Page/PerformanceTimeline events into the capture pipeline, and injectsinteraction.jsto emit click/key/scroll events viaRuntime.addBinding(with per-session rate limiting and click/key privacy suppression for sensitive fields).Implements derived page lifecycle signals (
network_idle,layout_settled,navigation_settled) via debounced timers, captures truncated textual response bodies (skipping binary), and adds periodic screenshot capture viaffmpegwith downscaling + rate limiting.Adds reconnection handling on upstream DevTools URL changes (disconnect/reconnect/failure events, backoff retries, cleanup of pending commands/requests), plus extensive unit tests and JSON fixtures to enforce CDP PDL field fidelity.
Updates the API service to construct the monitor with a
sloglogger and makesApiService.cdpMonitoran interface to allow stubbing in tests.Reviewed by Cursor Bugbot for commit 5465e59. Bugbot is set up for automated code reviews on this repo. Configure here.