Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Force LF in the working tree on every platform.
#
# Windows checks out CRLF by default (core.autocrlf=true). server.mjs starts
# with a `#!/usr/bin/env node` shebang, and Vite's SSR transform — which vitest
# runs the module through — fails to parse that shebang when the line ends in
# CRLF: the transform hoists the imports above it and then chokes on
# "Invalid Character `!`", so the whole suite dies with a SyntaxError before a
# single test runs. Pinning LF keeps the suite behaving identically everywhere.
* text=auto eol=lf

# Shell scripts must keep LF to stay executable on POSIX.
*.sh text eol=lf

*.tgz binary
8 changes: 7 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ on:

jobs:
test:
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}

strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
node-version: [20, 22, 24]

steps:
Expand All @@ -26,8 +28,12 @@ jobs:
- name: Install dependencies
run: npm ci

# Coverage thresholds are pinned at 100% in vitest.config.mjs, so this
# also fails the build if a change adds an untested line or branch.
- name: Run tests
run: npx vitest run --coverage

# The dependency tree is platform independent — auditing it once is enough.
- name: Security audit
if: matrix.os == 'ubuntu-latest' && matrix.node-version == 22
run: npm audit --audit-level=high
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ node_modules/
# Tests
test-ssh/
*.test.mjs
vitest.config.mjs
coverage/

# Generated files
Expand Down
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Security
- Resolved all 17 open `npm audit` advisories (12 high, 4 moderate, 1 low) that accumulated since the 1.3.7 cleanup. `npm audit` now reports zero vulnerabilities again. `npm audit fix` could not be used — it aborts with an internal npm error (`Cannot read properties of null (reading 'edgesOut')`) on this tree's `overrides` — so the fixes are pinned explicitly:
- Direct bumps, all within the existing semver range: `@modelcontextprotocol/sdk` 1.27.1 → 1.30.0, `vitest`/`@vitest/coverage-v8` 4.1.4 → 4.1.10, `@anthropic-ai/dxt` 0.2.5 → 0.2.6. These cleared the `vite`, `postcss` and `nanoid` advisories.
- Raised the existing `tmp` override from `>=0.2.4` to `>=0.2.6` (GHSA path traversal via unsanitized prefix/postfix), which cleared the whole `@anthropic-ai/dxt → @inquirer/prompts → @inquirer/editor → external-editor → tmp` chain.
- New overrides for the transitive HTTP-stack advisories reachable through `@modelcontextprotocol/sdk`: `brace-expansion`, `fast-uri`, `ip-address`, `hono`, `@hono/node-server`, `body-parser`, `qs`, `express-rate-limit`. Each is pinned to the lowest version that carries the fix. As in 1.3.7, none of this code is reachable from this package: it belongs to the SDK's HTTP/SSE transport, and mcp-ssh only ever loads `server/stdio.js`.
- Verified after the bumps: full suite green, `npm ci` reproducible from the lockfile, and the real server answers `initialize` and `tools/list` correctly over STDIO.

### Fixed
- **Multi-alias hosts (fixes #12)**: A host declared under several aliases (`Host docker-lxc hlab`) was unreachable under *any* of its names. `ssh-config@5` returns a plain string for a single-token value but an array of token objects (`{val, separator, quoted}`) once a directive carries more than one token; `extractHostsFromConfig` stored that array in `alias` verbatim, so every strict comparison downstream (`_assertKnownHostAlias`, `getHostInfo`, `getPasswordForHost`, `getAllKnownHosts`) compared a string against an array and never matched. The host was listed by `listKnownHosts` but rejected by the known-host gate before `ssh` was ever spawned. ssh-config values are now normalized once at parse time: `alias` keeps the first alias (output shape unchanged), a new `aliases` field carries the full list, and matching goes through a shared `hostMatchesAlias()` helper. Contributed by @badigit.
- **Wildcard blocks with negations**: `Host * !bastion` was emitted as a connectable host if it carried a `HostName`. The old `section.value !== '*'` check could not match a multi-token value, which is an array. Blocks consisting only of wildcards and negated patterns are now skipped as the defaults blocks they are.
- **Multi-token directives**: `ProxyCommand`, `SendEnv`, `IPQoS` and friends were surfaced in `listKnownHosts` output as arrays of token objects instead of readable strings. They are now flattened.

### Changed
- **Windows test suite**: 14 tests silently asserted POSIX-only behaviour (the `chmod 600` config check, the `/bin/sh` askpass helper, `detached`, a bare `ssh` as argv[0]) and failed when the suite ran on Windows. Both platform paths are now asserted explicitly by re-importing the module with `process.platform` faked, so the suite is meaningful and green on either OS. `SSH_BIN`/`SCP_BIN` are exported so tests assert against the binary the module actually resolved.
- **CI**: the test matrix now runs on `windows-latest` in addition to `ubuntu-latest`, across Node 20/22/24.
- **Coverage**: `server.mjs` is at 100% statements, branches, functions and lines, and `vitest.config.mjs` pins those thresholds so a change adding an untested line or branch fails the build.
- Dropped the `process.env.Path` fallback in `resolveExecutable()`: Node exposes `process.env` case-insensitively on Windows, so `process.env.PATH` already resolves a variable spelled `Path`. The fallback was unreachable.

## [1.3.8] - 2026-04-14

### Fixed
Expand Down
Loading
Loading