Do not open loopback computer URLs from a LAN tab - #11
Conversation
There was a problem hiding this comment.
This PR implements a security improvement by preventing loopback URLs from being opened when the page is accessed over LAN. The implementation properly adds validation for loopback addresses and surfaces error messages instead of failing silently.
Key changes reviewed:
- New loopback validation utilities with comprehensive test coverage
- Enhanced error handling for desktop link creation and pop-up blocking
- "Watch screen" feature added for LAN users as alternative to noVNC
- Vite proxy configuration updated to check both OMB_PORT and OGB_PORT
Critical change requested (1):
- Fix bracket removal regex to prevent potential security bypass
The approach is sound and addresses the stated problem effectively. Once the regex fix is applied, this will be ready to merge.
You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.
| /** noVNC is bound to 127.0.0.1:6080 on purpose. Opening that URL from a | ||
| * LAN browser hits the *client's* loopback, which has no viewer. */ | ||
| export function loopbackViewerUsable(hostname: string): boolean { | ||
| const host = hostname.replace(/^\[|\]$/g, "").toLowerCase(); |
There was a problem hiding this comment.
The regex /^\[|\]$/g only removes brackets at the start OR end but doesn't handle brackets in the middle of hostnames. Consider using /[\[\]]/g to remove all brackets, preventing bypass via hostnames like [127.0.0.1]foo which would currently pass validation.
| const host = hostname.replace(/^\[|\]$/g, "").toLowerCase(); | |
| const host = hostname.replace(/[\[\]]/g, "").toLowerCase(); |
) Adopts the open agentskills.io format (Apache-2.0 spec). Server v1: - fetch from GitHub (owner/repo, tree/blob/raw URLs) with the registry's discovery walk (skills/, .claude/skills, .agents/skills), size caps - markdown-only: scripts are recorded as skipped, never written — the Snyk/Koi registry audits found confirmed exfil payloads almost always in scripts - imports land DISABLED with provenance (source + sha256) and static red-flag scan (base64 blobs, curl|sh, invisible Unicode); a person enables after reading the full SKILL.md - enabled skills reach the bot like MEMORY.md does: a budgeted name+description index in the system prompt, files read on demand — plus symlinks into .claude/skills, .agents/skills and .grok/skills in the workspace so CLIs with native skill support load them themselves - API: list / import / read / enable / remove under /api/bots/:id/skills UI (import modal in bot settings) is the follow-up PR. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat: add secure credential request cards * fix: make credential card resume recoverable * fix: surface room credential resume failures * fix: drain credential resumes after terminal failures * fix: isolate room credential requests and decline retries
* feat: add section-scoped shared context * fix: guard unsaved section context
* feat: preview image attachments in app * fix: keep attachment preview keyboard-safe
* feat: add chat find and flat replies * fix: resolve reply quotes across branch history
milind-soni#425 made sidebar mascots mount paused — and exposed that the parked loop never draws: the SVG layers hold no expression until the first draw() positions them, so an idle bot's avatar rendered blank. The paused branch now paints the still face once, re-painting only when what it shows changes (state, pinned expression, gradient), then parks on the same 4Hz wake-poll. Animation stays opt-in; the resting pose is simply visible again. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* Add portable BotMRR package installs * Keep package link tests out of Vitest discovery * Add universal BotMRR Markdown imports * Document portable team playbooks
…i#346) * fix(linux): fail closed on unsafe local control startup * fix(desktop): keep optional connected apps off startup path * fix(linux): repair inherited DEB upgrade permissions * docs(linux): document the local-control safety hold * fix(ci): configure dependencies in DEB upgrade smoke * fix(linux): configure DEB Chromium sandbox * fix(linux): restore safe Xorg local control * fix(desktop): close review security and refresh races * fix(linux): isolate local control safety opt-in * docs(linux): explain private CUA cursor behavior * fix(linux): clean CUA runtime on termination signals * fix(linux): reap stale AppImage CUA stages * docs(linux): clarify release CUA coverage * test(linux): preserve packaged smoke diagnostics * ci(linux): normalize runner package parent * fix(linux): close final Ubuntu review gaps * fix(ci): fail closed before Ubuntu package install * fix(composio): enforce broker URL parity
…lind-soni#447) * Let Antigravity models control computers (mount the computer MCP) agy has no per-turn MCP flag and provably no project-level MCP config (1.1.19: embedded docs list only the global ~/.gemini/config/mcp_config.json and per-plugin files; agy mcp list ignores .gemini/{settings,mcp_config}.json in the cwd). So each turn upserts one key — openmausbot-computer — into the global file right before the spawn, preserving every other byte of the user's config and tolerating malformed JSON, and removes that key on the next computer-less turn so tools and box/control tokens cannot leak into later turns or the user's own agy sessions. Cloud boxes mount OpenMausBot's REST-to-MCP computer proxy (resolved via SPAWNED_PROXIES — never relative to the module, the 0.1.24 lesson); Local VM and VPS connections pass through as the stdio Cua command they already are. computerMcp is advertised only by full-auto instances: print mode has no interactive approval channel, and outside --dangerously-skip-permissions agy auto-denies tools that would prompt, so a non-fullAuto mount could never fire. localComputerMcp stays unset — the host desktop requires per-action human approval, which print mode cannot deliver in any mode. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: isolate Antigravity computer mounts * fix: reap settled Antigravity children * fix: keep MCP lease until child exit * fix: preserve Antigravity MCP ownership * fix: clear failed Antigravity turns --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…k actions (milind-soni#441) * feat(electron): right-click context menu with clipboard and spellcheck actions Adds a context-menu handler wiring Undo/Redo/Cut/Copy/Paste/ Paste-and-Match-Style/Select All plus spellcheck suggestions and copy-link, so text fields behave like native macOS apps. * refactor(electron): skip context menu when nothing is actionable; popup targets the source frame --------- Co-authored-by: Milind Soni <46266943+milind-soni@users.noreply.github.com>
…milind-soni#442) * feat(ui): composer attach button and per-bot permission mode selector Adds a paperclip button that opens a file picker feeding the shared attachment pipeline, and an Approve-for-me / Ask-for-approval pill that toggles autoApprove per bot without opening settings. The composer is restructured into two rows — text on top, controls below — matching common chat-app layouts. * feat(ui): remove the Always allow button from approval cards The per-bot permission mode selector in the composer (Ask for approval / Approve for me) is the single mechanism for reducing approval prompts; the per-program Always-allow grant duplicated it with a worse model. Allow-once and Deny remain. * Paint the resting face when a mascot mounts paused (milind-soni#444) milind-soni#425 made sidebar mascots mount paused — and exposed that the parked loop never draws: the SVG layers hold no expression until the first draw() positions them, so an idle bot's avatar rendered blank. The paused branch now paints the still face once, re-painting only when what it shows changes (state, pinned expression, gradient), then parks on the same 4Hz wake-poll. Animation stays opt-in; the resting pose is simply visible again. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * fix(composio): preload connected account state (milind-soni#445) * chore(release): bump version to 0.1.33 (milind-soni#446) * Add portable BotMRR Markdown playbooks (milind-soni#426) * Add portable BotMRR package installs * Keep package link tests out of Vitest discovery * Add universal BotMRR Markdown imports * Document portable team playbooks * fix(composio): accept empty authorization bodies (milind-soni#451) * ci: stop retaining disposable Linux packages (milind-soni#452) * fix(linux): harden Ubuntu upgrades and Xorg local control (milind-soni#346) * fix(linux): fail closed on unsafe local control startup * fix(desktop): keep optional connected apps off startup path * fix(linux): repair inherited DEB upgrade permissions * docs(linux): document the local-control safety hold * fix(ci): configure dependencies in DEB upgrade smoke * fix(linux): configure DEB Chromium sandbox * fix(linux): restore safe Xorg local control * fix(desktop): close review security and refresh races * fix(linux): isolate local control safety opt-in * docs(linux): explain private CUA cursor behavior * fix(linux): clean CUA runtime on termination signals * fix(linux): reap stale AppImage CUA stages * docs(linux): clarify release CUA coverage * test(linux): preserve packaged smoke diagnostics * ci(linux): normalize runner package parent * fix(linux): close final Ubuntu review gaps * fix(ci): fail closed before Ubuntu package install * fix(composio): enforce broker URL parity * Let Antigravity models control computers (mount the computer MCP) (milind-soni#447) * Let Antigravity models control computers (mount the computer MCP) agy has no per-turn MCP flag and provably no project-level MCP config (1.1.19: embedded docs list only the global ~/.gemini/config/mcp_config.json and per-plugin files; agy mcp list ignores .gemini/{settings,mcp_config}.json in the cwd). So each turn upserts one key — openmausbot-computer — into the global file right before the spawn, preserving every other byte of the user's config and tolerating malformed JSON, and removes that key on the next computer-less turn so tools and box/control tokens cannot leak into later turns or the user's own agy sessions. Cloud boxes mount OpenMausBot's REST-to-MCP computer proxy (resolved via SPAWNED_PROXIES — never relative to the module, the 0.1.24 lesson); Local VM and VPS connections pass through as the stdio Cua command they already are. computerMcp is advertised only by full-auto instances: print mode has no interactive approval channel, and outside --dangerously-skip-permissions agy auto-denies tools that would prompt, so a non-fullAuto mount could never fire. localComputerMcp stays unset — the host desktop requires per-action human approval, which print mode cannot deliver in any mode. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: isolate Antigravity computer mounts * fix: reap settled Antigravity children * fix: keep MCP lease until child exit * fix: preserve Antigravity MCP ownership * fix: clear failed Antigravity turns --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Milind Soni <46266943+milind-soni@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Kesley David <39314443+KesleyDavid@users.noreply.github.com> Co-authored-by: milind-soni <milindsoni201@gmail.com>
…tools only Every permission event was stamped approvalScope=local-computer whenever a bot had This computer enabled — including plain Bash/Edit asks. The server suppresses allowKey when a scope is set, so the Always-allow button never rendered for any tool on such bots. Now only mcp__computer* tools carry the scope; request.resolved looks the tool up from the pending ask.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ew-live-desktop feat(computer): open live desktop from preview
…y-foundation fix(ios): pair through every trusted route
…-status-skin-tokens fix(onboarding): use the success and warning tokens for status badges
…-rename fix(tasks): let the header picker actually rename
…-vm-20260822 Local VM: add secure two-up workspace and Chief status awareness
feat(channels): add separate task conversations
Review routine approvals without crossing provider boundaries
# Conflicts: # server/index.ts
feat(mcp): add Model Context Protocol (MCP) server for external agent orchestration
Users can explicitly ask a bot to list, schedule, update, pause, resume, run, or delete routines in chat. Every mutation remains inert until a durable, exact-detail confirmation card is confirmed, with ownership, stale-state, cloud-readiness, redaction, audit, recovery, and voice safeguards.
…a, model-shaped coercion, guiding errors (milind-soni#544) Field report (0.1.38, hours after milind-soni#540 shipped): a bot's propose_routine failed on every attempt — the model tried a 30-minute interval, was told no, fell back to "daily", failed again, then gave up ("the routine proposal tool is returning errors every time — it's not accepting any input"). Reproduced the whole pipeline against a live server: the endpoint and proxy are correct for valid payloads. What actually breaks in the field is the tool's advertised schema: schedule was a oneOf of two const-discriminated branches — exactly the JSON-Schema keywords several agent CLIs flatten or drop when converting MCP tools into their provider's function-call format (codex only began preserving oneOf in June 2026; other drivers still simplify). A model that never saw the branches guesses shapes forever, and every guess failed with a message that never taught it the right one. Three changes, all proxy-side — the harness dialect is untouched: - The schedule schema is now one flat object (type enum once|weekly|daily + at/time/weekdays), free of oneOf/const/format, so it survives any conversion. Rules live in descriptions and are enforced with words. - normalizeScheduleInput coerces what models actually send: "daily" becomes weekly-on-all-seven-days on the wire, JSON-string schedules are parsed, weekday names are case-folded and short names (mon..sun) expanded. - Unsupported shapes now answer with instructions instead of a wall: a sub-day interval is named as unsupported with the closest alternatives, weekly-without-weekdays points at "daily", unknown types list the three supported shapes with examples. Verified end-to-end against a live server: daily / stringified / short-cap payloads now produce confirmation cards, interval and missing-weekdays get guiding errors, and the valid-weekly path is byte-identical. Mutation check: disabling the daily alias fails the new tests. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat(chat): dock the composer over the transcript Bubbles scroll into the pill instead of clipping on a black bar. The pill has no hairline; app-ground under it keeps text from painting below the dock. * docs(chat): before/after captures for the composer dock --------- Co-authored-by: Max <ajsdaksfjhs@gmail.com>
…sion eats composition keywords (milind-soni#547) Codified from the milind-soni#544 field failure: schedule was a oneOf of const-branches, several engines' MCP-to-provider converters flattened it, and models guessed shapes forever. The rule, the coercion posture, and the errors-must-teach posture now live next to the driver SPI guidance so the next tool surface doesn't relearn it in production. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…m-9dfe Co-authored-by: mhand <matthewhand@users.noreply.github.com>
Fork main is now even with milind-soni/OpenMausBot main (plus this delta doc and prior merge commits). Feature work stays on the open draft PRs. Co-authored-by: mhand <matthewhand@users.noreply.github.com>
Keep loopback-viewer guards. Apply them to the new openDesktop path and keep Watch screen next to the per-bot noVNC link. Co-authored-by: mhand <matthewhand@users.noreply.github.com>
Reviewer brief
This is a draft on
matthewhand/OpenMausBot, not an upstream PR tomilind-soni/OpenMausBot. One concern: noVNC / join URLs bound to127.0.0.1must not be opened from a LAN browser tab.Read
src/lib/loopback-viewer.tsfirst. The UI files only call those helpers.Why this exists
The Local VM viewer is bound to
127.0.0.1:6080on the host. Opening that URL from another machine hits that client's loopback, which has no viewer. Cloud desktop URLs are fine from anywhere.What to look at
src/lib/loopback-viewer.tsloopbackViewerUsable/canOpenExternalUrl. Treats IPv4-mapped forms (::ffff:127.0.0.1and Node's[::ffff:7f00:1]) as loopback. Invalid URLs return false.src/lib/loopback-viewer.test.tssrc/lib/frame-src.tsdata:URLs so Watch/VM previews do not double-prefix.src/components/LocalComputerSection.tsxsrc/components/ComputerPanel.tsxvite.config.tsOMB_PORT || OGB_PORT. No bearer inject.Security notes (please check these)
Authorizationon the Vite/apiproxy.POST /api/local-computer/screenshot.How to verify
pnpm exec vitest run src/lib/loopback-viewer.test.ts src/lib/frame-src.test.ts pnpm typecheckManual:
http://127.0.0.1:…): Open noVNC still appears.vite.config.tshas no bearer-token inject.Out of scope
LAN auth, MCP, TTS, comm popups. README marketing PNGs are stale separately (computer-panel still shows 3-way Runs on / Routines).
Process note
Local commits:
9e83916,a3ebc17,94ea252inC:\\OpenMausBot-pr-lan-viewer. Do not merge tomilind-soni/OpenMausBotfrom this draft.