security: harden hosts.json and supply chain install paths#60
Merged
asieduernest12 merged 15 commits intodamoahdominic:multihostfrom Apr 4, 2026
Merged
security: harden hosts.json and supply chain install paths#60asieduernest12 merged 15 commits intodamoahdominic:multihostfrom
asieduernest12 merged 15 commits intodamoahdominic:multihostfrom
Conversation
Co-Authored-By: FletcherFrimpong <[email protected]>
…ties - config.ts: remove unsafe-eval from Content Security Policy - statusController.ts: HTML-escape version strings before innerHTML injection - statusHtml.ts: sanitize maintainerName/URL before inserting into innerHTML - extension.ts: use imported cp module instead of require(); redact API keys from logs - openclaw-ssh/connection.ts: validate file paths to reject null bytes and non-absolute paths - status.ts: add localResourceRoots to restrict webview resource loading - apps/web: upgrade Next.js to 16.2.1 to fix CSRF bypass and HTTP smuggling CVEs Co-Authored-By: FletcherFrimpong <[email protected]>
JWT was stored in plaintext globalState (a JSON file on disk), readable by any process running as the same OS user or any VS Code extension. Changes: - All reads: context.globalState.get(OCC_JWT_KEY) → context.secrets.get() - All writes: context.globalState.update() → context.secrets.store/delete() - One-time migration on activate: moves existing JWT from globalState to SecretStorage, then deletes plaintext copy - Smoke test no longer logs any part of the JWT - Deep-link URI handler (occode://auth) stores token in SecretStorage JWT is now encrypted at rest via OS keychain (Keychain on macOS, Credential Manager on Windows, libsecret on Linux). Co-Authored-By: FletcherFrimpong <[email protected]>
Before running npm install, OCC now: 1. Fetches package metadata from registry.npmjs.org 2. Downloads the tarball to a temp file 3. Verifies SHA-512 hash against dist.integrity (SRI format) 4. Only installs from the verified local file — no re-download All three install paths are covered: - Main npm install path - Sudo retry path (re-downloads and re-verifies) - nvm fallback path If the hash does not match, installation is aborted with a clear error message. Temp files are always cleaned up after install. Protects against: MITM attacks, CDN compromise, corrupted downloads. Co-Authored-By: FletcherFrimpong <[email protected]>
…export The site uses Next.js static export (output: "export"), which means server-side fetches only run at build time. Moving the GitHub releases API fetch to a client-side useEffect ensures users always get the latest release download URLs at runtime.
Replace the unsafe `curl | bash` pattern with a 4-step verified install: download script to temp file, fetch SHA-256 checksum from a separate domain (releases.openclaw.sh), verify with sha256sum, then execute only if the hash matches. Temp files are cleaned up on both success and failure paths. Protects against MITM attacks, DNS hijacking, and CDN compromise — an attacker must now compromise two independent origins simultaneously. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Added quotes to HTML attributes and anon=1 param to include all contributors. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…er trust Mitigates malicious extension attack vector where any VS Code extension could read ~/.occ/hosts.json to exfiltrate SSH connection details (host, user, keyPath). Three hardening measures: 1. Connection configs now stored in VS Code SecretStorage (OS keychain), with automatic migration from plaintext. hosts.json retains only a type stub for adapter routing. 2. hosts.json written with 0600 permissions (owner-only). 3. registerHostAdapter() now requires extension ID, validates against trusted allowlist, and prompts user approval for unknown adapters. All registrations logged to output channel. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…bash Closes remaining supply chain gaps identified in security review: 1. Post-Node.js-install fallback now uses SHA-512 verified tarball instead of bare `npm install -g openclaw`. 2. Local installer scripts (Unix/Windows) no longer pipe remote scripts directly into shell. Scripts are downloaded to temp files, SHA-256 checksums fetched and verified before execution. 3. Removed `npm install -g openclaw@latest` fallback from update prompt — only suggests `openclaw update` now. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Mitigates gateway exposure attack (port 18789 reachable on LAN): 1. Add bind-address safety check — when gateway is running on a local host, probe the port on a non-loopback interface. If reachable, show a warning that the gateway is exposed to the network. 2. All health check HTTP requests now use 127.0.0.1 explicitly instead of localhost (which could resolve to a non-loopback address). 3. Add readGatewayToken() helper for future use — reads the auth token from openclaw.json so health checks can include authentication. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Closes the first-connect MITM vulnerability in the SSH adapter: 1. StrictHostKeyChecking changed from accept-new to yes — SSH now rejects connections to hosts not in known_hosts. 2. Setup wizard verifies host keys before first connect: fetches fingerprint via ssh-keyscan, shows it in a modal dialog, and only adds to known_hosts after user explicitly trusts the host. 3. testConnection() refuses unknown hosts — directs users to the setup wizard for proper fingerprint verification. 4. Helper functions exported: isHostKnown(), fetchHostFingerprint(), addHostToKnownHosts() for consistent host key management. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Mitigates fake-update and release-tampering attack vectors: 1. Version strings from npm registry now validated against strict semver pattern — rejects crafted/malicious version responses. Non-200 HTTP responses also rejected (prevents redirect-based spoofing). 2. Removed remaining npm install -g openclaw@latest fallback from statusController auto-update prompt. 3. Website download page now validates that release asset URLs originate from github.com/objects.githubusercontent.com domains before linking — prevents tampered release metadata from redirecting users to malicious download servers. 4. Website shows "Verify checksums (SHA-256)" link when a checksums file is present in the GitHub release assets. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…ions Mitigates container escape attack vector: 1. All OCC-created containers now run with --cap-drop ALL --cap-add NET_BIND_SERVICE --security-opt no-new-privileges. This drops dangerous default capabilities and prevents privilege escalation via setuid binaries. 2. Runtime security audit on container connect: inspects the container for dangerous configurations (running as root, --privileged mode, Docker socket mounted) and warns the user. Covers both OCC-created and user-provided containers. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Two critical fixes: 1. Remove webview catch-all command execution fallback in statusController.ts and home.ts. Previously, any unrecognized webview message with a `command` field was passed directly to vscode.commands.executeCommand() — allowing a compromised webview to execute arbitrary VS Code commands. The fallback clauses are removed; only explicitly handled commands are accepted. 2. Replace all remaining curl-pipe-bash patterns in installCli() across all 4 connection files (local, docker, ssh, localDefault). Install scripts are now downloaded to temp files, checksums fetched and verified via sha256sum, then executed. Temp files cleaned up on success and failure. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Three high-severity fixes: 1. openUrl webview handler now validates URLs against a domain allowlist (occ.mba.sh, github.com, openclaw.ai, etc.) and rejects non-http(s) protocols. Prevents phishing via compromised webview content. 2. API keys no longer passed as CLI arguments (visible in ps, docker logs, shell history). All runSetup() methods now pass the key via OPENCLAW_API_KEY environment variable instead. 3. buildExecEnv() no longer spreads the entire process.env to child processes. Each adapter now uses safeExecEnv() which allowlists only necessary variables (PATH, HOME, SHELL, etc.) SSH adapter keeps SSH_AUTH_SOCK; Docker keeps DOCKER_HOST. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
~/.occ/hosts.jsonto VS Code SecretStorage (OS keychain, encrypted, per-extension isolated). Existing data auto-migrates on activation.registerHostAdapter()now requires extension ID, checks against a trusted allowlist, and prompts users before allowing unknown extensions to register as adapters. All registrations logged.hosts.jsonwrites (defense-in-depth).curl | bash).npm install -g openclaw@latestfallback from update prompts.Motivation
Security review identified two attack vectors:
~/.occ/hosts.jsonto exfiltrate SSH connection details, then register as an adapter with zero verification.npm install -g openclaw,curl | bash).Test plan
~/.occ/hosts.jsoncontains only{ "type": "ssh" }stub (no host/user/keyPath)stat -f '%Lp' ~/.occ/hosts.json→600🤖 Generated with Claude Code