go-webview/docs/security-attack-vector-mapping.md
Virgil 6a261bdf16 docs(cdp): add attack vector mapping
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-23 13:39:39 +00:00

17 KiB

Security Attack Vector Mapping

Date: 2026-03-23

Notes:

  • CODEX.md was not present in this repository when this mapping was prepared, so repo-specific conventions were taken from CLAUDE.md.
  • Thin wrappers are grouped with the underlying sink when they share the same trust boundary and behaviour. Examples: ActionSequence.Navigate is grouped with NavigateAction.Execute and Webview.Navigate.
  • This is a mapping document only. No mitigations or code changes are proposed here.

Caller-Controlled Inputs

Function File:line Input source What it flows into Current validation Potential attack vector
WithDebugURL, NewCDPClient, ListTargets, ListTargetsAll, GetVersion webview.go:81
cdp.go:78
cdp.go:351
cdp.go:372
cdp.go:387
Caller-supplied Chrome debug URL http.Get(debugURL + "/json"), http.Get(debugURL + "/json/version"), json.Unmarshal, and, in NewCDPClient, websocket.DefaultDialer.Dial to the returned webSocketDebuggerUrl No scheme, host, auth, status-code, or body-size validation; JSON shape trusted after json.Unmarshal SSRF against arbitrary internal hosts; unauthenticated trust in a hostile CDP endpoint; malicious /json can steer the code into a WS connection to an attacker host; large responses can cause memory pressure
CDPClient.NewTab cdp.go:289 Caller-supplied URL for the new tab; remote /json/new response body Raw string concatenation into debugURL + "/json/new?" + url, then http.Get, json.Unmarshal, and websocket.DefaultDialer.Dial to the returned WS URL No URL escaping; no scheme or destination checks; no status-code or body-size validation Query manipulation against the debug endpoint; opening attacker-chosen pages in the browser; SSRF through the debug service; hostile response can redirect the WS dial
CDPClient.Call, CDPClient.Send cdp.go:163
cdp.go:267
Caller-supplied CDP method names and params JSON serialisation to the live DevTools WebSocket No allow-list or schema validation beyond JSON encoding Arbitrary CDP command execution, including powerful browser control primitives; blind fire-and-forget misuse via Send; broader blast radius if an untrusted component can reach this API
CDPClient.OnEvent cdp.go:205 Caller-supplied event names and callbacks Stored in handlers, later invoked by dispatchEvent for browser-originated CDP events No validation or deduplication Unbounded handler registration; browser event floods can amplify into caller callback fan-out and goroutine pressure
Webview.Navigate, NavigateAction.Execute, ActionSequence.Navigate webview.go:152
actions.go:43
actions.go:446
Caller-supplied navigation URL or action field CDP Page.navigate, then waitForLoad polling via Runtime.evaluate("document.readyState") No scheme, host, or destination validation Browser-mediated SSRF to internal services; navigation to sensitive schemes such as file:, data:, javascript:, or others if Chrome permits; automation redirection into attacker-controlled flows
Webview.Click, ClickAction.Execute, ActionSequence.Click webview.go:168
webview.go:704
actions.go:22
actions.go:436
Caller-supplied CSS selector or action field DOM.querySelector; either CDP mouse events or JS fallback document.querySelector(%q)?.click() Only existence and bounding-box checks; JS fallback uses %q for selector quoting Expensive selector abuse against large DOMs; arbitrary interaction with attacker-chosen elements; destructive clicks inside a privileged browser session
Webview.Type, TypeAction.Execute, ActionSequence.Type webview.go:176
webview.go:740
actions.go:33
actions.go:441
Caller-supplied selector and text JS focus script, then Input.dispatchKeyEvent for each rune Selector is JS-quoted with %q; text is unbounded Arbitrary input injection into forms and widgets; credential stuffing into the current page; large payloads can generate high event volume
Webview.QuerySelector webview.go:184
webview.go:569
Caller-supplied selector DOM.getDocument, DOM.querySelector, DOM.describeNode, DOM.getBoxModel, then ElementInfo returned No selector validation beyond CDP/browser parsing; result fields only type-asserted DOM metadata exfiltration from an untrusted page; attacker-controlled attribute values returned to the caller; selector complexity abuse
Webview.QuerySelectorAll, Webview.QuerySelectorAllAll webview.go:192
webview.go:200
webview.go:604
Caller-supplied selector DOM.querySelectorAll, then getElementInfo per returned node No selector validation beyond CDP/browser parsing; no cap on result count Large node sets can amplify CPU and memory use; DOM data exfiltration; selector complexity abuse
Webview.WaitForSelector, WaitForSelectorAction.Execute, ActionSequence.WaitForSelector webview.go:280
webview.go:517
actions.go:74
actions.go:456
Caller-supplied selector Repeated Runtime.evaluate("!!document.querySelector(%q)") until timeout Selector is JS-quoted with %q; no complexity or rate limits beyond the 100 ms ticker Polling on hostile/large DOMs can create steady CPU load; attacker controls when the wait resolves
Webview.Evaluate webview.go:272
webview.go:541
Caller-supplied JavaScript source CDP Runtime.evaluate with returnByValue: true, result returned to caller No validation; this surface is intentionally arbitrary Direct arbitrary JS execution in the page; DOM/session data exfiltration; page mutation; leverage of any privileged browser APIs exposed to the page context
Webview.GetHTML webview.go:324 Optional caller-supplied selector Fixed or selector-based JS passed to Runtime.evaluate, HTML returned Selector is JS-quoted with %q; no output size limit Full-document or targeted DOM exfiltration; large HTML payloads can cause memory pressure; selector complexity abuse
Webview.SetViewport webview.go:349 Caller-supplied width and height CDP Emulation.setDeviceMetricsOverride No range checks Extreme dimensions can drive browser resource use or renderer instability
Webview.SetUserAgent webview.go:363 Caller-supplied User-Agent string CDP Emulation.setUserAgentOverride No allow-list or content filtering in package code Header spoofing, app feature-gating bypass, and downstream log pollution if Chrome accepts unusual characters
Webview.UploadFile actions.go:471 Caller-supplied selector and local file paths DOM.setFileInputFiles Selector must resolve; file paths are not normalised, existence-checked, or restricted Sensitive local file selection followed by browser-side upload or exfiltration if the page submits the form
Webview.DragAndDrop actions.go:490 Caller-supplied source and target selectors querySelector for both ends, then Input.dispatchMouseEvent sequence Existence and bounding-box checks only Arbitrary drag/drop interactions in a privileged session; selector complexity abuse
ScrollAction.Execute actions.go:85 Caller-populated X/Y values Raw JS window.scrollTo(%d, %d) via Webview.evaluate Numeric formatting only Large values can produce unexpected page behaviour; lower-risk than the arbitrary-script surface but still direct page control
ScrollIntoViewAction.Execute, FocusAction.Execute, BlurAction.Execute, ClearAction.Execute, SelectAction.Execute, CheckAction.Execute, SetAttributeAction.Execute, RemoveAttributeAction.Execute, SetValueAction.Execute actions.go:97
actions.go:109
actions.go:121
actions.go:133
actions.go:153
actions.go:172
actions.go:378
actions.go:391
actions.go:404
Caller-populated selector, value, attribute, or checked-state fields Constructed JS passed to Webview.evaluate; several rows also dispatch input/change events String inputs are JS-quoted with %q; no semantic allow-list or size checks Arbitrary DOM mutation and synthetic event dispatch; selector complexity abuse; low direct string-injection risk because %q quoting is used
HoverAction.Execute, DoubleClickAction.Execute, RightClickAction.Execute actions.go:189
actions.go:216
actions.go:263
Caller-populated selectors querySelector plus CDP mouse events, with JS fallbacks for double/right click Existence and bounding-box checks; fallback selectors are JS-quoted with %q Arbitrary pointer interaction, including double-click and context-menu behaviour inside a privileged session; selector complexity abuse
PressKeyAction.Execute actions.go:307 Caller-populated key name or text Input.dispatchKeyEvent; unknown keys are sent as raw "text" Small allow-list for common keys; all other input is passed through Synthetic keystroke injection, control-character delivery, and high-volume key event generation
AngularHelper.NavigateByRouter angular.go:214 Caller-supplied Angular router path JS router.navigateByUrl(%q) followed by Zone stability wait Path is JS-quoted with %q; no route allow-list Route manipulation inside a privileged SPA session; app-specific workflow or authorisation bypass if an untrusted caller controls the path
AngularHelper.GetComponentProperty angular.go:331 Caller-supplied selector and property name JS querySelector, window.ng.probe(element).componentInstance, then component[%q] returned Selector in querySelector and property name are quoted, but selector is also interpolated raw into an error string with %s Arbitrary component state read; JS injection if a crafted selector forces the error path and breaks out of the raw error string
AngularHelper.SetComponentProperty angular.go:353 Caller-supplied selector, property name, and value JS querySelector, component[%q] = %v, then ApplicationRef.tick() Property name is quoted; selector also appears raw in an error string; formatJSValue only safely quotes strings, bools, and nil, and uses raw %v otherwise Arbitrary component state mutation; JS injection via the raw selector error path or via crafted non-primitive values rendered with raw %v
AngularHelper.CallComponentMethod angular.go:384 Caller-supplied selector, method name, and args JS querySelector, component[%q](%s), then ApplicationRef.tick() Method name is quoted at call time but also appears raw in an error string; args use formatJSValue Arbitrary component method invocation; JS injection via selector/method-name error paths or crafted args rendered with raw %v
AngularHelper.GetService angular.go:453 Caller-supplied Angular DI token name JS injector.get(%q) followed by JSON.stringify/parse, returned to caller Service name is JS-quoted; no size or content limits on serialised output Exfiltration of DI service state from debug-enabled Angular apps; large services can cause serialisation or memory pressure
AngularHelper.WaitForComponent angular.go:480 Caller-supplied selector Repeated JS querySelector plus window.ng.probe until timeout Selector is JS-quoted with %q Polling on hostile DOMs can create steady CPU load; attacker controls when the wait resolves
AngularHelper.DispatchEvent angular.go:517 Caller-supplied selector, event name, and detail payload JS new CustomEvent(%q, { bubbles: true, detail: %s }), then dispatchEvent Event name is quoted; selector also appears raw in an error string; detail uses formatJSValue Synthetic event injection into Angular app logic; JS injection via the raw selector error path or crafted detail rendered with raw %v
AngularHelper.GetNgModel angular.go:543 Caller-supplied selector JS querySelector, optional Angular debug probe, value/text returned to caller Selector is JS-quoted with %q Exfiltration of form or model values from the current page
AngularHelper.SetNgModel angular.go:570 Caller-supplied selector and value JS element.value = %v, input/change events, and ApplicationRef.tick() Selector also appears raw in an error string; value uses formatJSValue Arbitrary model mutation; business-logic and event injection; JS injection via raw selector error path or crafted value rendered with raw %v
ConsoleWatcher.WaitForMessage console.go:168 Caller-supplied filter pattern plus browser-originated console text Substring scans over stored and future console messages No pattern-length cap or escaping Large attacker-controlled log lines combined with long caller-supplied patterns can amplify CPU use; hostile pages can control when the wait resolves
FormatConsoleOutput console.go:524 Caller- or browser-supplied ConsoleMessage fields Raw fmt.Sprintf into output lines No sanitisation of text, URL, or prefix content Log forging and terminal escape propagation if the formatted output is printed or persisted verbatim

Browser- and CDP-Originated Inputs

Function File:line Input source What it flows into Current validation Potential attack vector
CDPClient.readLoop cdp.go:212 Raw WebSocket frames from the connected CDP peer json.Unmarshal into cdpResponse or cdpEvent, then pending response channels or dispatchEvent No explicit frame-size limit, schema validation, origin check, or auth check; malformed frames are mostly ignored Memory pressure from large frames; silent desynchronisation; spoofed responses/events from a hostile endpoint; event-flood delivery into higher layers
CDPClient.dispatchEvent cdp.go:255 CDP event method and params forwarded from readLoop One goroutine per registered handler Clones the handler slice but does not rate-limit or bound concurrency Goroutine exhaustion and scheduler pressure under high-volume event streams
Webview.Screenshot webview.go:245 Browser-supplied base64 screenshot payload Base64 decode into a byte slice returned to caller Type assertion and base64 decode only; no size cap Large screenshot payloads can cause memory pressure or decode-time DoS
Webview.handleConsoleEvent webview.go:453 Runtime.consoleAPICalled event params from the page via CDP Builds ConsoleMessage and appends it to the Webview ring buffer Best-effort type assertions only; no sanitisation of text, URL, or stack data Log forging, terminal escape propagation, and bounded memory pressure up to consoleLimit
NewConsoleWatcher, ConsoleWatcher.handleConsoleEvent console.go:33
console.go:246
Runtime.consoleAPICalled event params from the page via CDP Builds ConsoleMessage, stores it in the watcher buffer, then notifies registered handlers Best-effort type assertions only; bounded by limit; no sanitisation Caller handler fan-out on attacker-controlled log data; bounded memory pressure; log forging
NewExceptionWatcher, ExceptionWatcher.handleException console.go:371
console.go:468
Runtime.exceptionThrown event params from the page via CDP Extracts exception text and stack trace, appends to ew.exceptions, then calls registered handlers Best-effort type assertions only; no sanitisation; no retention limit Unbounded memory growth under exception spam; attacker-controlled stack traces and text reaching caller sinks; handler fan-out DoS
ExceptionWatcher.WaitForException console.go:434 Stored and future browser-originated exception data Returns the latest ExceptionInfo to the caller No validation beyond prior parsing Attacker controls exception timing and payload content that may be logged or acted on by the caller
Webview.GetURL, Webview.GetTitle webview.go:288
webview.go:306
Page-controlled window.location.href and document.title values Fixed Runtime.evaluate calls returning strings to the caller Only result type assertions Low-volume data exfiltration from the current page; attacker controls returned strings
AngularHelper.GetRouterState angular.go:251 Page-controlled Angular router state returned from Runtime.evaluate Parsed into AngularRouterState and returned to caller Type assertions on expected string and map fields only Exfiltration of route params, query params, and fragments from the SPA; large values can increase memory use

Local Configuration Inputs That Amplify Exposure

Function File:line Input source What it flows into Current validation Potential attack vector
WithTimeout, AngularHelper.SetTimeout, WaitAction.Execute, ActionSequence.Wait webview.go:93
angular.go:27
actions.go:59
actions.go:451
Caller-supplied durations Context deadlines and time.After waits No range checks Excessively long values can pin goroutines and prolong exposure windows; zero or negative values can short-circuit synchronisation logic
WithConsoleLimit, ConsoleWatcher.SetLimit webview.go:102
console.go:72
Caller-supplied message limits In-memory retention size for console buffers No lower or upper bound checks Very large limits increase memory retention under noisy pages; low or negative values do not disable capture cleanly