registry  /  @linsoai/browser-relay  /  1.1.2

@linsoai/browser-relay@1.1.2

Universal browser relay — control your Chrome from any AI agent via HTTP API or MCP. No auth, no cloud.

AI Security Review

scanned 4d ago · by lpm-firewall-ai

Global npm install creates and starts a user-level autostart daemon that exposes no-auth browser automation. The daemon can evaluate JavaScript in attached Chrome tabs and inspect page/network state.

Static reason
High-risk behavior combination matched malicious policy.
Trigger
npm global install of @linsoai/browser-relay@1.1.2
Impact
Persistent local service can provide powerful browser/session control to any local process that can reach 127.0.0.1:18795; remote control is also supported when configured.
Mechanism
lifecycle-created launchd/systemd persistence for browser-control daemon
Attack narrative
On global install, the postinstall script writes a launchd plist or systemd user unit and immediately starts Browser Relay. That service persists across login/crash and exposes local unauthenticated HTTP APIs that forward Chrome DevTools commands through the extension, including page JavaScript evaluation, navigation, downloads, console, screenshots, and network observation. The package purpose is disclosed, but lifecycle autostart of a browser-control daemon is a high-risk persistence behavior.
Rationale
The blockable behavior is lifecycle-triggered persistence of a powerful no-auth browser-control daemon, not classic credential exfiltration. Although package-aligned and guarded to global installs, it creates OS autostart entries during npm install and processes browser/session data through local and optional remote control surfaces.
Evidence
package.jsonserver/install.jsserver/uninstall.jsserver/relay-server.jsserver/cli.jsserver/mcp-server.jsserver/remote-protocol.jsextension/background.js~/Library/LaunchAgents/org.browser-relay.service.plist~/.config/systemd/user/browser-relay.service
Network endpoints3
127.0.0.1:18795ws://127.0.0.1:18795/extensionrelay.linso.ai

Decision evidence

public snapshot
AI called this Malicious at 86.0% confidence as Dangerous Capability with low false-positive risk.
Evidence for warning
  • package.json runs postinstall server/install.js and postuninstall server/uninstall.js
  • server/install.js global install writes ~/Library/LaunchAgents/org.browser-relay.service.plist or ~/.config/systemd/user/browser-relay.service
  • server/install.js enables/starts the service with launchctl/systemctl during npm lifecycle
  • server/relay-server.js exposes unauthenticated browser control APIs including /api/eval, /api/navigate, /api/download/start
  • server/relay-server.js captures console/network data and redacts only selected headers
  • server/remote-protocol.js and extension/background.js support remote relay via https://relay.linso.ai
Evidence against
  • server/install.js skips service registration for non-global installs
  • server/install.js only checks existing agent skill copies and does not overwrite .claude/.codex/.cursor skill directories
  • relay host defaults to 127.0.0.1
  • network capture redacts Authorization/Cookie/Set-Cookie headers
  • remote relay appears opt-in via extension configuration/device id
  • behavior is broadly aligned with documented browser relay purpose
Behavioral surface
Source
ChildProcessCryptoEnvironmentVarsFilesystemNetworkShellWebSocket
Supply chain
HighEntropyStringsUrlStrings
ManifestNo manifest risk signals triggered.
scanned 14 file(s), 229 KB of source, external domains: 127.0.0.1, deploy.workers.cloudflare.com, device.local, example.com, github.com, relay.linso.ai, relay.local, www.apple.com, www.npmjs.com

Source & flagged code

4 flagged · loading source
package.jsonView file
scripts.postinstall = node server/install.js
High
Install Time Lifecycle Scripts

Package defines install-time lifecycle scripts.

package.jsonView on unpkg
scripts.postinstall = node server/install.js
Medium
Ambiguous Install Lifecycle Script

Install-time lifecycle script is not statically allowlisted and needs review.

package.jsonView on unpkg
server/install.jsView file
1Install-time AI-agent control hijack evidence: L1: #!/usr/bin/env node L2: import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; L3: import { dirname, join } from "node:path"; ... L64: L65: mkdirSync(dirname(plistDst), { recursive: true }); L66: writeFileSync(plistDst, plistContent); L67: ... L100: L101: mkdirSync(dirname(serviceDst), { recursive: true }); L102: writeFileSync(serviceDst, serviceContent); L103: ... L123: const AGENT_SKILL_ROOTS = [ Payload evidence from skill/SKILL.md: L32: ``` L33: Relay URL: http://127.0.0.1:18795 L34: WebSocket: ws://127.0.0.1:18795/extension ... L118: Header: Content-Type: application/json L119: Body: { "url": "https://example.com", "tabId?": "optional-target-id" } L120: ``` ... L187: ### 8. browser_screenshot L188: Capture a PNG screenshot (base64). L189: ```
Critical
Ai Agent Control Hijack

Install-time source drops package-supplied AI-agent/MCP control files or instructions.

server/install.jsView on unpkg · L1
5import { homedir, platform } from "node:os"; L6: import { execSync } from "node:child_process"; L7: L8: const __dirname = dirname(fileURLToPath(import.meta.url)); L9: const RELAY_DIR = dirname(__dirname); ... L67: L68: // Use bootstrap/bootout — `launchctl load/unload` is the legacy API and L69: // often silently fails or returns EBADEXEC on macOS 13+. bootout first for ... L153: // a daemon hijacking their machine. L154: const isGlobal = process.env.npm[redacted] === "true"; L155: if (!isGlobal) { ... L182: try {
Medium
Install Persistence

Source writes installer persistence such as shell profile or service configuration.

server/install.jsView on unpkg · L5

Findings

1 Critical1 High5 Medium5 Low
CriticalAi Agent Control Hijackserver/install.js
HighInstall Time Lifecycle Scriptspackage.json
MediumAmbiguous Install Lifecycle Scriptpackage.json
MediumNetwork
MediumEnvironment Vars
MediumInstall Persistenceserver/install.js
MediumStructural Risk Force Deep Review
LowNon Install Lifecycle Scripts
LowScripts Present
LowFilesystem
LowHigh Entropy Strings
LowUrl Strings