feat: SSH host key verification (TOFU)#488
Merged
billchurch merged 31 commits intomainfrom Feb 26, 2026
Merged
Conversation
Add HostKeyServerStoreConfig, HostKeyClientStoreConfig, and HostKeyVerificationConfig interfaces. Add hostKeyVerification property to SSHConfig interface.
Add default hostKeyVerification config (disabled, hybrid mode, prompt on unknown). Add WEBSSH2_SSH_HOSTKEY_* environment variable mappings. Add clone function for deep-copying the config.
The resolveHostKeyMode function expands mode shorthand (server, client, hybrid) into serverStore.enabled and clientStore.enabled flags. Explicit store flags from file/env config override mode defaults. Includes 10 tests covering all modes, overrides, immutability, and default values.
Add better-sqlite3 as runtime dependency and @types/better-sqlite3 as dev dependency for the SQLite-backed host key store.
Read-only SQLite store for server-side host key verification. Supports lookup (trusted/mismatch/unknown) and getAll operations. Degrades gracefully when database file is missing. Includes 16 tests covering all lookup scenarios, port/algorithm discrimination, missing DB, and close behavior.
HostKeyService coordinates host key verification using server-side and client-side stores. Provides config getters, server store lookup delegation, and SHA-256 fingerprint computation matching OpenSSH format. Includes 13 tests covering getters, store delegation, fingerprint determinism, and close behavior.
Add HostKeyService to the Services interface and create it in the factory when host key verification is enabled. Add Zod validation schema for HostKeyVerificationConfig to ensure the config passes through schema validation without being stripped.
Add HOSTKEY_VERIFY, HOSTKEY_VERIFY_RESPONSE, HOSTKEY_VERIFIED, HOSTKEY_MISMATCH, HOSTKEY_ALERT, and HOSTKEY_REJECTED events for host key verification client-server communication.
Add createHostKeyVerifier factory that returns an async callback for SSH2 hostVerifier option. Implements the full decision tree: server store lookup, client store verification with socket events, timeout handling, and unknownKeyAction fallback (alert/reject/prompt). Includes 9 unit tests covering all branches of the verification flow.
Add socket property to SSHConfig interface for host key verification. SSHServiceImpl now accepts optional HostKeyService via constructor and creates the hostVerifier callback when both the service and socket are available. The factory creates HostKeyService before SSHServiceImpl. Updated verifier to use SSH2 HostVerifier callback-style API with extractAlgorithm helper to parse algorithm from raw key buffer.
Emit hostKeyVerification config (enabled, clientStoreEnabled, unknownKeyAction) in the permissions payload after successful auth. Pass socket reference through SSHConfig for hostVerifier communication. Update socket contract types, test mocks, and contract assertions to include the new hostKeyVerification field in permissions.
…issions Update the legacy SocketAdapter.emitPermissions to also include hostKeyVerification config in the permissions payload for consistency with the service-based socket adapter.
Prevents runtime 'Cannot read properties of undefined' errors in socket-v2 tests when emitAuthSuccess accesses hostKeyVerification.
Tests the full server-side verification pipeline end-to-end using a temp SQLite DB, real HostKeyService, and createHostKeyVerifier with mock sockets. Covers all five decision tree branches: trusted key, mismatch, unknown+reject, unknown+alert, and feature disabled.
Create scripts/host-key-seed.ts CLI tool for managing the SQLite host key database. Supports --host (SSH probe), --hosts (batch file), --known-hosts (OpenSSH import), --list, --remove, and --help commands. Add "hostkeys" npm script to package.json. Also fix lint issues in the integration test file (proper Socket type import, vi import from vitest).
- Fix 17 lint errors (duplicate imports, inline import() types, negated condition, naming convention, structuredClone) - Add disconnect cleanup to awaitClientVerification to prevent 30s resource linger on client disconnect - Add input validation for hostkey:verify-response payload (untrusted client data at security boundary) - Replace ASCII diagrams with mermaid sequence diagrams and flowchart in protocol reference
- Add hostKeyVerification section to CONFIG-JSON.md with all options, default config, use case examples, and seeding script usage - Add 6 WEBSSH2_SSH_HOSTKEY_* env vars to ENVIRONMENT-VARIABLES.md with mode behavior table and Docker example - Add SOCKET_EVENTS host key constants to CONSTANTS.md - Add hostKeyVerification block to default config example
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
…n hostkeys script - Add hostKeyVerification to /ssh/config HTTP endpoint so the client receives the config at page load, before any socket connection. This allows the Trusted Host Keys settings section to be visible immediately rather than only after authenticating. - Also emit hostKeyVerification in an early permissions event on socket connect (before auth) for the socket-connected-but-not-yet-authenticated case. - Make session-related permissions fields optional in the socket contract to support partial pre-auth emissions. - Add WEBSSH2_SSH_HOSTKEY_DB_PATH env var support to the hostkeys CLI script (resolution: --db arg > env var > config.json > default). - Add data/ to .gitignore.
Add tests for SSH config handler hostKeyVerification response, socket adapter permissions emission, and host-key-seed CLI helpers. Export extractDbPathFromConfig, resolveDbPath, and parseArgs for testability. Fix S2871 unstable .sort() in contract tests, update socket contract assertions to account for pre-auth permissions emission order.
- Replace hardcoded /tmp paths with :memory: in test configs (S5443) - Use void for side-effect-only constructor calls (S1848) - Add localeCompare to .sort() for reliable string ordering (S2871) - Extract shared test fixtures to reduce cross-file duplication - Extract helper functions to reduce self-duplication in verifier and integration tests
Remove unnecessary type assertion in socket-contracts and fix negated condition in host-key-seed tests.
Update rollup from 4.57.1 to 4.59.0 to address high severity arbitrary file write vulnerability. Exception to 2-week age-out policy granted due to severity. Update SECURITY.md with assessment and review dates.
…4623, S6557, S4043, S7763, S4325, S6594, S7781, S7755, S7784, S7924) Address 50 SonarQube issues spanning CRITICAL, MAJOR, and MINOR severities: - Remove unnecessary void operator from test describes (S3735, 12 issues) - Extract loadFileConfig helper to reduce cognitive complexity in config.ts (S3776) - Use String#endsWith instead of regex suffix checks in scripts (S6557) - Use toReversed() to avoid in-place array mutation (S4043) - Remove redundant undefined argument where default param exists (S4623) - Fix CSS contrast ratio for WCAG AA compliance (S7924) - Convert import-then-export to export...from syntax (S7763) - Remove unnecessary type assertions in test files (S4325) - Use RegExp.exec() instead of String.match() (S6594) - Use String#replaceAll() instead of replace with global regex (S7781) - Prefer .at(-1) over [.length - 1] indexing (S7755) - Use structuredClone() instead of JSON.parse(JSON.stringify()) (S7784) - Fix TypeScript index signature access and argument type safety (TS4111, TS2345) - Add structuredClone to ESLint globals for Node.js 22+
…nd use structuredClone (S7784)
|
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
Implements SSH host key verification using the Trust On First Use (TOFU) model. This adds server-side infrastructure for verifying SSH host keys during connection, with three configurable modes: server-only (SQLite), client-only (browser localStorage), and hybrid (server-first with client fallback).
Key changes:
HostKeyVerificationConfigwith Zod schema, env var mapping (WEBSSH2_HOSTKEYVERIFY_*), andresolveHostKeyModefor mode-to-store expansionHostKeyStorewrapping better-sqlite3 for persistent server-side key storage with lookup/save/delete operationshostVerifiercallbacks that integrate with Socket.IO for interactive client verificationhostkey:*Socket.IO events for verify/response/verified/mismatch/alert/rejected flowsscripts/seed-host-keys.tsfor pre-populating the SQLite storeTesting
Test plan
npm run lintpasses with 0 errorsnpm run typecheckpassesnpm run testpasses all 1,214 testsnpm run buildsucceedsmode: 'hybrid'— client prompt appearsmode: 'alert'— connection proceeds with warningmode: 'server'— keys stored in SQLite only