The crawler port hides Selenium types, but it does not create a secure browser platform. Its operations share an implicit “current page,” making it a trusted internal primitive rather than a multi-tenant request API.
1. Four current operations
| Method | Input | Effect/output |
|---|---|---|
OpenUrlAsync | URL, optional TenantId | Mutates current browser page |
FindElementAsync | ID/name/class/tag/CSS/XPath | Element snapshot |
ScreenshotAsync | optional TenantId | URL, title, PNG bytes |
ExecuteScriptAsync | script and arguments | Arbitrary script result |
TenantId is not an isolation boundary. Open ignores it and Screenshot uses it only in a Debug log.
2. Implicit session race
The port has no browser/session handle and cannot bind open→find→script→screenshot into one atomic work unit. A singleton SDK service can leak page state, cookies, and results across concurrent callers.
3. Current URL validation
The adapter rejects only blank URLs. It has no scheme, host, resolved-address, port, redirect, user-info, DNS-rebinding, download-size, or response-type policy.
Passing user input directly therefore creates SSRF exposure to cloud metadata, private networks, link-local services, and the local host.
4. Policy wrapper
// ① Require an absolute normalized HTTP(S) URI without user-info.Uri target = await urlPolicy.ParseAndValidateAsync(command.Url, cancellationToken);
// ② Validate every resolved address against public/tenant allowlists.await urlPolicy.AssertPublicOrTenantAllowlistedAsync(target, cancellationToken);
// ③ The current port does not repeat these controls.Result opened = await crawler.OpenUrlAsync( new CrawlerOpenUrlRequest(target.ToString(), trustedTenantId), cancellationToken);Redirects require per-hop revalidation; validating the initial address alone is insufficient.
5. Script boundary
ExecuteScriptAsync only rejects blank script. It defines no template catalog, operation allowlist, sandbox, or output budget. User-controlled script is arbitrary execution inside the browser context.
Expose server-owned operations such as ReadInvoiceTotal; map them to fixed scripts and schemas. Arguments should be bounded JSON primitives and validated selectors.
// ① Resolve only an operation registered by the server.CrawlerScriptDefinition definition = scriptCatalog.Get(command.Operation);definition.Validate(command.Arguments, currentTarget);
// ② The request never supplies raw JavaScript.return await crawler.ExecuteScriptAsync( new CrawlerExecuteScriptRequest(definition.Script, command.Arguments), ct);6. Locator and data leakage
CSS and XPath can be computationally expensive. XPath can also reach content outside an intended page region. Constrain strategy, length, count, and root scope per operation.
Element projection includes the full href, which can contain tokens or query secrets. Redact or normalize it before logs, audit, or client output.
7. Serializable output
The adapter converts only a directly returned IWebElement to a string. Collections, dictionaries, or nested objects can still contain Selenium objects and fail serialization or leak implementation details.
A production contract should allow only explicit JSON scalars and bounded arrays. Reject unknown object graphs.
8. Cancellation and timeout
Synchronous Selenium calls are wrapped in Task.Run(..., cancellationToken). Cancellation can prevent scheduling but does not interrupt a WebDriver call that has started.
Configure WebDriver page/script timeouts, work-unit deadlines, driver disposal after timeout, process watchdogs, bounded queues, and tenant concurrency quotas. Do not describe the token as immediate underlying cancellation.
9. Error translation
The adapter catches WebDriver, no-such-element/not-found, and timeout exceptions, returns ProviderFailed, and includes the raw exception message.
Owner code should classify target rejection, navigation timeout, missing element, script failure, and broken session. Raw details belong in restricted diagnostics; unknown exceptions still need global handling.
10. Isolation design
A session should bind tenant, actor, allowed domains, creation time, deadline, and exclusive ownership. Cleanup must cover cookies, local storage, downloads, cache, and temporary profiles. High-sensitivity work should use disposable processes/containers.
11. Test matrix
Cover loopback, private IPv4/IPv6, link-local and metadata targets; encoded IPs, user-info, redirects and rebinding; concurrent tenant isolation; cookie/storage cleanup; timed-out driver retirement; arbitrary script and huge-output rejection; nested element results; and error-detail redaction.
No such tests exist in current source, so the port should remain a trusted internal primitive.
A consumer design review must identify:
- whether a page may contain another tenant’s data;
- the exact domain and redirect allowlists;
- who owns each browser session;
- which cookies or credentials enter the browser;
- which fixed scripts and selectors are allowed;
- maximum navigation and script time;
- maximum screenshot and script-output bytes;
- cleanup evidence after success, timeout, and crash;
- response fields that require redaction;
- the fallback behavior when no browser is available.
12. Operations and source check
Limit browser CPU, memory, file descriptors, and processes. Pin browser/driver compatibility, patch promptly, deny network egress by default, isolate downloads, and monitor queue, sessions, timeouts, crashes, and cleanup failures.
Treat browser unavailability as an explicit degraded capability, never as permission to bypass the owner workflow.
rg -n "OpenUrlAsync|ExecuteScriptAsync|Task.Run|IWebElement" \ src/Platform/ToolConnectors -g '*.cs'