17 KiB
17 KiB
Security Attack Vector Mapping
Date: 2026-03-23
Notes:
CODEX.mdwas not present in this repository when this mapping was prepared, so repo-specific conventions were taken fromCLAUDE.md.- Thin wrappers are grouped with the underlying sink when they share the same trust boundary and behaviour. Examples:
ActionSequence.Navigateis grouped withNavigateAction.ExecuteandWebview.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:81cdp.go:78cdp.go:351cdp.go:372cdp.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:163cdp.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:152actions.go:43actions.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:168webview.go:704actions.go:22actions.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:176webview.go:740actions.go:33actions.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:184webview.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:192webview.go:200webview.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:280webview.go:517actions.go:74actions.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:272webview.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:97actions.go:109actions.go:121actions.go:133actions.go:153actions.go:172actions.go:378actions.go:391actions.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:189actions.go:216actions.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:33console.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:371console.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:288webview.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:93angular.go:27actions.go:59actions.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:102console.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 |