ssh-mcp-pro 1.1.5 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +47 -7
  3. package/SECURITY.md +4 -0
  4. package/SECURITY_DECISIONS.md +32 -1
  5. package/dist/agent-bin.js +0 -0
  6. package/dist/connector-profile.d.ts +19 -2
  7. package/dist/connector-profile.d.ts.map +1 -1
  8. package/dist/connector-profile.js +36 -14
  9. package/dist/connector-profile.js.map +1 -1
  10. package/dist/http-security.d.ts.map +1 -1
  11. package/dist/http-security.js +19 -4
  12. package/dist/http-security.js.map +1 -1
  13. package/dist/http-server-lifecycle.d.ts +17 -0
  14. package/dist/http-server-lifecycle.d.ts.map +1 -0
  15. package/dist/http-server-lifecycle.js +122 -0
  16. package/dist/http-server-lifecycle.js.map +1 -0
  17. package/dist/http-server-router.d.ts +28 -0
  18. package/dist/http-server-router.d.ts.map +1 -0
  19. package/dist/http-server-router.js +108 -0
  20. package/dist/http-server-router.js.map +1 -0
  21. package/dist/http-server-runtime.d.ts +21 -0
  22. package/dist/http-server-runtime.d.ts.map +1 -0
  23. package/dist/http-server-runtime.js +415 -0
  24. package/dist/http-server-runtime.js.map +1 -0
  25. package/dist/http-session-registry.d.ts.map +1 -1
  26. package/dist/http-session-registry.js.map +1 -1
  27. package/dist/index.js +12 -5
  28. package/dist/index.js.map +1 -1
  29. package/dist/mcp.d.ts +1 -1
  30. package/dist/mcp.js +1 -1
  31. package/dist/prompts.d.ts +2 -11
  32. package/dist/prompts.d.ts.map +1 -1
  33. package/dist/prompts.js +8 -1
  34. package/dist/prompts.js.map +1 -1
  35. package/dist/remote/agent-cli.d.ts.map +1 -1
  36. package/dist/remote/agent-cli.js +20 -1
  37. package/dist/remote/agent-cli.js.map +1 -1
  38. package/dist/remote/agent-connection-lifecycle.d.ts +47 -0
  39. package/dist/remote/agent-connection-lifecycle.d.ts.map +1 -0
  40. package/dist/remote/agent-connection-lifecycle.js +304 -0
  41. package/dist/remote/agent-connection-lifecycle.js.map +1 -0
  42. package/dist/remote/agent-executor.d.ts.map +1 -1
  43. package/dist/remote/agent-executor.js +89 -67
  44. package/dist/remote/agent-executor.js.map +1 -1
  45. package/dist/remote/agent-handler.d.ts +22 -8
  46. package/dist/remote/agent-handler.d.ts.map +1 -1
  47. package/dist/remote/agent-handler.js +151 -98
  48. package/dist/remote/agent-handler.js.map +1 -1
  49. package/dist/remote/config.d.ts.map +1 -1
  50. package/dist/remote/config.js +14 -0
  51. package/dist/remote/config.js.map +1 -1
  52. package/dist/remote/control-plane.d.ts.map +1 -1
  53. package/dist/remote/control-plane.js +16 -0
  54. package/dist/remote/control-plane.js.map +1 -1
  55. package/dist/remote/oauth-handler.d.ts +6 -0
  56. package/dist/remote/oauth-handler.d.ts.map +1 -1
  57. package/dist/remote/oauth-handler.js +61 -1
  58. package/dist/remote/oauth-handler.js.map +1 -1
  59. package/dist/remote/policy-update-guard.d.ts +14 -0
  60. package/dist/remote/policy-update-guard.d.ts.map +1 -0
  61. package/dist/remote/policy-update-guard.js +27 -0
  62. package/dist/remote/policy-update-guard.js.map +1 -0
  63. package/dist/remote/policy.d.ts.map +1 -1
  64. package/dist/remote/policy.js +13 -2
  65. package/dist/remote/policy.js.map +1 -1
  66. package/dist/remote/safe-file.d.ts +6 -0
  67. package/dist/remote/safe-file.d.ts.map +1 -0
  68. package/dist/remote/safe-file.js +192 -0
  69. package/dist/remote/safe-file.js.map +1 -0
  70. package/dist/remote/store.d.ts.map +1 -1
  71. package/dist/remote/store.js.map +1 -1
  72. package/dist/remote/types.d.ts +5 -0
  73. package/dist/remote/types.d.ts.map +1 -1
  74. package/dist/remote/websocket.d.ts +38 -13
  75. package/dist/remote/websocket.d.ts.map +1 -1
  76. package/dist/remote/websocket.js +90 -126
  77. package/dist/remote/websocket.js.map +1 -1
  78. package/dist/render-http.js +2 -1
  79. package/dist/render-http.js.map +1 -1
  80. package/dist/resources.d.ts +2 -7
  81. package/dist/resources.d.ts.map +1 -1
  82. package/dist/resources.js.map +1 -1
  83. package/dist/retry.d.ts.map +1 -1
  84. package/dist/retry.js +4 -2
  85. package/dist/retry.js.map +1 -1
  86. package/dist/server-http.d.ts +1 -1
  87. package/dist/server-http.d.ts.map +1 -1
  88. package/dist/server-http.js +1 -421
  89. package/dist/server-http.js.map +1 -1
  90. package/dist/session.d.ts +4 -0
  91. package/dist/session.d.ts.map +1 -1
  92. package/dist/session.js +38 -15
  93. package/dist/session.js.map +1 -1
  94. package/dist/tools/connector.provider.d.ts.map +1 -1
  95. package/dist/tools/connector.provider.js +2 -1
  96. package/dist/tools/connector.provider.js.map +1 -1
  97. package/dist/types.d.ts.map +1 -1
  98. package/dist/types.js.map +1 -1
  99. package/docs/agent-runtime-config.md +34 -0
  100. package/docs/development/api-stability.md +44 -0
  101. package/docs/development/coding-standards.md +41 -0
  102. package/docs/development/commit-conventions.md +35 -0
  103. package/docs/development/dependency-management.md +60 -0
  104. package/docs/development/deprecation-policy.md +29 -0
  105. package/docs/development/local-setup.md +46 -0
  106. package/docs/development/release-process.md +31 -0
  107. package/docs/development/testing-policy.md +47 -0
  108. package/docs/explanation/architecture.md +44 -0
  109. package/docs/how-to/README.md +14 -0
  110. package/docs/how-to/configure-ssh-policy.md +50 -0
  111. package/docs/openssf-evidence.md +75 -0
  112. package/docs/openssf-gap-analysis.md +63 -0
  113. package/docs/openssf-proposal-links.md +37 -0
  114. package/docs/professionalization-plan.md +81 -0
  115. package/docs/reference/README.md +21 -0
  116. package/docs/reference/cli.md +41 -0
  117. package/docs/reference/compatibility.md +46 -0
  118. package/docs/reference/configuration.md +72 -0
  119. package/docs/remote-mcp-hardening.md +27 -0
  120. package/docs/repo-maturity-report.md +565 -0
  121. package/docs/security/assurance-case.md +58 -0
  122. package/docs/security/dependency-audit-exceptions.json +4 -0
  123. package/docs/security/dependency-audit-exceptions.md +15 -0
  124. package/docs/security/input-validation.md +54 -0
  125. package/docs/security/release-integrity.md +28 -0
  126. package/docs/security/scanning-policy.md +50 -0
  127. package/docs/security/secrets-management.md +49 -0
  128. package/docs/security/threat-model.md +65 -0
  129. package/docs/testing.md +24 -0
  130. package/docs/troubleshooting.md +56 -0
  131. package/docs/tutorials/getting-started.md +84 -0
  132. package/mcp.json +1 -1
  133. package/package.json +86 -70
  134. package/registry/ssh-mcp-pro/mcp.json +1 -1
  135. package/server.json +3 -3
@@ -0,0 +1,46 @@
1
+ # Local Setup
2
+
3
+ The authoritative setup steps are in
4
+ [CONTRIBUTING.md#setup](../../CONTRIBUTING.md#setup) — this page doesn't duplicate them,
5
+ it adds the one-command shortcuts.
6
+
7
+ ## Fastest path
8
+
9
+ ```bash
10
+ git clone https://github.com/oaslananka/ssh-mcp-pro.git
11
+ cd ssh-mcp-pro
12
+ task install # or: corepack enable && corepack prepare pnpm@11.5.1 --activate && pnpm install --frozen-lockfile
13
+ task verify # lint + format:check + typecheck + test + build
14
+ ```
15
+
16
+ [Taskfile.yml](../../Taskfile.yml) wraps the same pnpm scripts CONTRIBUTING.md documents
17
+ — use whichever you prefer; there is no behavioral difference between `task lint` and
18
+ `pnpm run lint`.
19
+
20
+ ## What `pnpm run prepare` does
21
+
22
+ Configures `core.hooksPath=.githooks` so pre-commit/pre-push hooks
23
+ (`pnpm run hook:pre-commit`, `pnpm run hook:pre-push`) run automatically. This runs
24
+ automatically after `pnpm install` via the `prepare` script — you don't need to invoke
25
+ it manually unless hooks stop working.
26
+
27
+ ## Docker-based integration tests
28
+
29
+ Integration/E2E tests exercise a real SSH fixture via Docker, not a mock:
30
+
31
+ ```bash
32
+ pnpm run docker:ssh-fixture:up
33
+ pnpm run test:integration
34
+ pnpm run docker:ssh-fixture:down
35
+ ```
36
+
37
+ See [docker-compose.yml](../../docker-compose.yml) and
38
+ [Dockerfile.test](../../Dockerfile.test).
39
+
40
+ ## No `.devcontainer` (by design, for now)
41
+
42
+ This repo doesn't ship a `.devcontainer/`. Given the toolchain is one `pnpm install`
43
+ away from working and Docker is only needed for integration tests, this was assessed as
44
+ optional rather than required — see "Developer experience maturity" in
45
+ [docs/repo-maturity-report.md](../repo-maturity-report.md). Revisit if Windows/macOS
46
+ contributors report toolchain friction that a devcontainer would solve.
@@ -0,0 +1,31 @@
1
+ # Release Process
2
+
3
+ ## Prepare a version
4
+
5
+ Release Please v5 prepares Conventional Commit-driven version updates on `main`. A release PR updates `CHANGELOG.md`, package, registry metadata, client readiness metadata and embedded version strings together. Merge a release PR only after the normal protected-branch checks pass.
6
+
7
+ When an unreleased version reaches `main`, the `Release` workflow preflights the **frozen dependency installation, full quality check, dependency freshness, package build, SBOM, checksums and GitHub artifact attestations** before Release Please creates the tag. It then attaches that exact prebuilt package and SBOM to the GitHub Release and dispatches an immutable-tag GHCR build. Ordinary main pushes without a new version do not repeat the heavy preflight.
8
+
9
+ ## One-click npm publication
10
+
11
+ **Actions → Release → Run workflow → main → Run workflow** is the **manual npm publish button**. No extra version input, stage job or automatic npm publish on release PR merge is involved.
12
+
13
+ The `Publish current GitHub Release to npm` job identifies the `package.json` version on `main`, locates its immutable GitHub Release, downloads the already-built `.tgz` and checksum, verifies the tag's source/version, checks SHA-256, package metadata and the GitHub attestation, then calls **`npm publish` directly with GitHub-hosted Actions OIDC**. A version already public on npm is not published twice. After confirming that exact npm version is public, it dispatches the matching MCP Registry publication job. npm Trusted Publisher must authorize `.github/workflows/release.yml` and permit **direct publication**, not stage-only. This is a provider-side prerequisite, not a second GitHub workflow.
14
+
15
+ GitHub does not override npm account/package security settings. If the npm publisher is configured for stage-only access or another mandatory owner restriction, the direct publish job fails clearly instead of silently staging or bypassing security. Avoid publishing historical versions to work around an npm account restriction; repair the publisher configuration through authorized account settings.
16
+
17
+ ## GHCR and MCP Registry
18
+
19
+ The release workflow triggers `.github/workflows/docker.yml` with the immutable `ssh-mcp-pro-vX.Y.Z` tag. GHCR must grant this repository's GitHub Actions **Write** access to the existing `ghcr.io/oaslananka/ssh-mcp-pro` package. Package access is managed through **GitHub Packages → Package settings → Manage Actions access**. Docker publishing verifies the release tag and package version before pushing both platform tags.
20
+
21
+ The MCP Registry job requires an existing GitHub Release and the *same publicly visible npm version*. It skips versions already registered and uses GitHub OIDC. There is no daily staged-publication reconciliation job. The manual npm publish workflow dispatches the MCP Registry publication only after npm confirms the version is public.
22
+
23
+ ## Verify and troubleshoot
24
+
25
+ - `gh release view ssh-mcp-pro-vX.Y.Z --json assets` — the prepared GitHub Release.
26
+ - `gh run list --workflow release.yml` — release preparation and manual npm publication status.
27
+ - `npm view ssh-mcp-pro@X.Y.Z version` — independently verify actual npm publication.
28
+ - `gh run list --workflow docker.yml` and `gh api users/oaslananka/packages/container/ssh-mcp-pro` — GHCR status.
29
+ - `gh run list --workflow publish-mcp-registry.yml` — MCP Registry publication.
30
+
31
+ Do not confuse a successful GitHub Release (or npm production environment deployment event) with a successful public npm/GHCR publication. Check the actual registry records before announcing an ecosystem release.
@@ -0,0 +1,47 @@
1
+ # Testing Policy
2
+
3
+ ## Test layers
4
+
5
+ Defined in [vitest.config.ts](../../vitest.config.ts) as separate projects:
6
+
7
+ | Project | Location | Run with |
8
+ | --- | --- | --- |
9
+ | Unit | `test/unit/**` | `pnpm test` |
10
+ | Integration | `test/integration/**` | `pnpm run test:integration` |
11
+ | Windows command-path integration | `test/integration/windows-ssh.integration.test.ts` | `pnpm run test:integration:windows` |
12
+ | E2E | `test/e2e/**` | `pnpm run test:e2e` |
13
+ | Performance | `test/perf/**` | `pnpm run test:perf` (baseline: `test:perf:baseline`) |
14
+
15
+ Integration and E2E projects run sequentially (single worker, no file parallelism) —
16
+ they exercise a real SSH fixture (see `scripts/docker-ssh-fixture.mjs` and
17
+ [docker-compose.yml](../../docker-compose.yml)) and aren't safe to parallelize against
18
+ shared fixture state. Protected CI verifies the Docker fixture before each Linux suite and then checks the generated JUnit report to ensure the env-gated tests executed instead of being silently skipped. Failure runs retain sanitized container state, fixture logs, and JUnit evidence for 14 days.
19
+
20
+ The `Windows Command-Path Integration` job executes PowerShell-capable command selection and Windows package-manager paths locally on a Windows runner. It does not claim a network connection to a real Windows OpenSSH server.
21
+
22
+ ## Coverage policy
23
+
24
+ `pnpm run test:coverage` enforces the thresholds in `vitest.config.ts`: 85% branches,
25
+ 85% functions, 90% lines, 90% statements repo-wide, with a scoped 75/80/85/85 threshold
26
+ for `src/remote/**` (the remote control-plane surface, which has more integration-only
27
+ paths that are harder to unit-test in isolation). These thresholds are a CI gate, not a
28
+ suggestion — `check:quality`'s downstream `check` script fails the build below them.
29
+
30
+ ## Mutation testing
31
+
32
+ `pnpm run test:mutation` runs [Stryker](../../stryker.conf.mjs) against specific line
33
+ ranges in the highest-consequence files: `auth.ts`, `policy.ts`, `safety.ts`,
34
+ `config.ts`, `session.ts`, `http-security.ts`, `oauth.ts`, and the `remote/*` control
35
+ plane. Thresholds are 80% mutation score to be considered healthy, 60% as the floor
36
+ (`break: null`, i.e. advisory rather than a hard CI failure today). See
37
+ [docs/testing.md](../testing.md) for the rationale behind the line-range allowlist and
38
+ promotion criteria for adding more files to it.
39
+
40
+ ## What a PR is expected to include
41
+
42
+ Per [CONTRIBUTING.md](../../CONTRIBUTING.md): new functionality needs accompanying
43
+ tests, and `pnpm run check` (which includes `test:coverage`) must pass before a PR is
44
+ considered ready. There is currently no CI gate that *requires* a human reviewer's
45
+ approval before merge (see the branch-protection gap in
46
+ [docs/repo-maturity-report.md](../repo-maturity-report.md)) — treat the automated gate
47
+ as necessary, not sufficient.
@@ -0,0 +1,44 @@
1
+ # Explanation: Architecture
2
+
3
+ This page is explanation-oriented (the "why"), per
4
+ [Diátaxis](https://diataxis.fr/explanation/). For the authoritative system diagram,
5
+ component breakdown, and ADRs, see [ARCHITECTURE.md](../../ARCHITECTURE.md) — this page
6
+ doesn't duplicate that content, it frames it.
7
+
8
+ ## Why an MCP server for SSH, specifically
9
+
10
+ Model Context Protocol gives an LLM client a structured, typed set of tools instead of
11
+ free-form shell access. ssh-mcp-pro's core design bet is that "give the model SSH" is
12
+ unsafe as a primitive, but "give the model a curated set of SSH-backed tools, each with
13
+ its own policy check" is a workable middle ground between "no remote access" and
14
+ "unrestricted remote shell." That bet is why the codebase has a dedicated `policy`/
15
+ `safety` layer sitting between tool dispatch and the SSH session, rather than tools
16
+ calling `node-ssh` directly.
17
+
18
+ ## Why defaults are deny-by-default
19
+
20
+ Every default documented in [SECURITY_DECISIONS.md](../../SECURITY_DECISIONS.md) —
21
+ strict host-key verification, no root login, no raw `sudo`, no destructive commands or
22
+ filesystem operations — exists because the honest threat model for this project is "an
23
+ LLM-driven client with SSH credentials, potentially acting on ambiguous or adversarial
24
+ instructions." See [docs/security/threat-model.md](../security/threat-model.md) for the
25
+ full reasoning. Architecturally, this means the safety checks live close to the tool
26
+ registry (so no tool can bypass them by construction), not scattered per-tool.
27
+
28
+ ## Why there's a separate "remote control plane"
29
+
30
+ `src/remote/*` and the `ssh-mcp-pro-agent` binary exist because not every deployment
31
+ wants the MCP server itself reachable from the public internet. The control plane lets
32
+ an operator run a broker that mediates access, so the SSH-capable server can stay on a
33
+ private network while remote clients (ChatGPT connectors, Claude connectors) talk to the
34
+ broker instead. See [docs/remote-mcp-hardening.md](../remote-mcp-hardening.md) for the
35
+ operational guide and [ARCHITECTURE.md](../../ARCHITECTURE.md) for where this sits in
36
+ the request flow.
37
+
38
+ ## Why tool profiles exist
39
+
40
+ `SSH_MCP_TOOL_PROFILE` (`full`, `remote-safe`, `chatgpt`, `claude`,
41
+ `remote-readonly`, `remote-broker`) exists because "expose every tool" is the right
42
+ default for a trusted local stdio client, but the wrong default for a remote connector
43
+ serving a broader audience. Profiles are an allowlist, not a denylist, so adding a new
44
+ profile can never accidentally widen an existing one's exposure.
@@ -0,0 +1,14 @@
1
+ # How-to Guides
2
+
3
+ How-to guides are goal-oriented: they assume you already know the basics (see the
4
+ [tutorial](../tutorials/getting-started.md) if not) and get you to a specific outcome.
5
+ Organized per the [Diátaxis](https://diataxis.fr/how-to-guides/) model.
6
+
7
+ - [How to run ssh-mcp-pro with Docker](../docker.md)
8
+ - [How to onboard a remote device / agent](../adding-a-device.md)
9
+ - [How to harden a public/remote MCP deployment](../remote-mcp-hardening.md)
10
+ - [How to set the SSH host-key and policy defaults](configure-ssh-policy.md)
11
+
12
+ If something's actually broken rather than a task you're trying to accomplish, see
13
+ [Troubleshooting](../troubleshooting.md) instead. If you're looking for exhaustive
14
+ option listings instead of a task, see [Reference](../reference/README.md).
@@ -0,0 +1,50 @@
1
+ # How to configure the SSH policy
2
+
3
+ This guide walks through the most common policy adjustments. Every setting here is one
4
+ row in the [environment variable reference](../../README.md#configuration) — this guide
5
+ just groups them by task.
6
+
7
+ ## Loosen host-key checking for a lab environment
8
+
9
+ By default `SSH_MCP_HOST_KEY_POLICY=strict`, which requires every host to already be in
10
+ `SSH_MCP_KNOWN_HOSTS_PATH`. For a throwaway lab environment where you accept the risk:
11
+
12
+ ```bash
13
+ export SSH_MCP_HOST_KEY_POLICY=accept-new
14
+ ```
15
+
16
+ Do not set this to `insecure` outside of a fully isolated test environment — see
17
+ [SECURITY_DECISIONS.md](../../SECURITY_DECISIONS.md) for why `strict` is the default.
18
+
19
+ ## Allow a specific destructive operation
20
+
21
+ Destructive commands and filesystem operations (like `fs_rmrf`) are denied by default.
22
+ To allow them for a specific deployment:
23
+
24
+ ```bash
25
+ export SSH_MCP_ALLOW_DESTRUCTIVE_COMMANDS=true
26
+ export SSH_MCP_ALLOW_DESTRUCTIVE_FS=true
27
+ ```
28
+
29
+ Prefer narrowing scope with `SSH_MCP_COMMAND_ALLOW`/`SSH_MCP_COMMAND_DENY` and
30
+ `SSH_MCP_PATH_ALLOW_PREFIXES`/`SSH_MCP_PATH_DENY_PREFIXES` instead of a blanket allow,
31
+ where possible.
32
+
33
+ ## Restrict which hosts can be targeted at all
34
+
35
+ ```bash
36
+ export SSH_MCP_ALLOWED_HOSTS="bastion.example.com,10.0.0.0/8"
37
+ ```
38
+
39
+ ## Use a policy file instead of many environment variables
40
+
41
+ ```bash
42
+ export SSH_MCP_POLICY_FILE=/etc/ssh-mcp-pro/policy.json
43
+ export SSH_MCP_POLICY_MODE=enforce
44
+ ```
45
+
46
+ Set `SSH_MCP_POLICY_MODE=explain` first in a non-production environment to see what the
47
+ policy *would* block without actually enforcing it, before flipping to `enforce`.
48
+
49
+ See [docs/security/input-validation.md](../security/input-validation.md) for how these
50
+ allow/deny lists are evaluated.
@@ -0,0 +1,75 @@
1
+ # OpenSSF Best Practices — Evidence
2
+
3
+ Self-assessment against the [OpenSSF Best Practices Passing criteria](https://www.bestpractices.dev/en/criteria/0),
4
+ current as of 2026-07-03. This is a repo-side evidence log, not an official OpenSSF
5
+ artifact — the actual badge is issued by submitting these answers at
6
+ [bestpractices.dev](https://www.bestpractices.dev/). See `.bestpractices.json` for the
7
+ same answers in a structured tracking form used to prepare that submission.
8
+
9
+ Status legend: `Met` / `Partially met` / `Unmet` / `N/A`, matching the badge site's own
10
+ vocabulary.
11
+
12
+ ## Basics
13
+
14
+ | Criterion | Status | Evidence |
15
+ | --- | --- | --- |
16
+ | Project has a public repository with a version-controlled history | Met | github.com/oaslananka/ssh-mcp-pro |
17
+ | Project uses a common distributed version control system (Git) | Met | Git |
18
+ | Project has FLOSS license | Met | `LICENSE` (MIT), `REUSE.toml` |
19
+ | License is OSI-approved | Met | MIT |
20
+ | Documentation: basic description of purpose | Met | README.md intro |
21
+ | Documentation: how to obtain/provide feedback and contribute | Met | CONTRIBUTING.md, SUPPORT.md |
22
+ | Interim versions/releases are identifiable | Met | Conventional Commits + release-please tagging (pending first tag — see gap analysis) |
23
+
24
+ ## Change control
25
+
26
+ | Criterion | Status | Evidence |
27
+ | --- | --- | --- |
28
+ | Public version-controlled source repository | Met | GitHub |
29
+ | Repository tracks changes with commit-level granularity | Met | Git history |
30
+ | Releases have version numbers following a documented convention | Met | SemVer via release-please |
31
+ | Project has release notes for each new major/minor release | Partially met | `CHANGELOG.md` follows Keep a Changelog; no release has actually been cut yet to verify end-to-end |
32
+
33
+ ## Reporting
34
+
35
+ | Criterion | Status | Evidence |
36
+ | --- | --- | --- |
37
+ | Project has a process for reporting bugs | Met | Issue templates, `.github/ISSUE_TEMPLATE/config.yml` |
38
+ | Project has a documented vulnerability reporting process | Met | `SECURITY.md` — private advisories, 7-day SLA |
39
+ | Project acknowledges vulnerability reports within a reasonable time | Unmet (no data) | No vulnerability report has been received yet to measure against the documented SLA |
40
+
41
+ ## Quality
42
+
43
+ | Criterion | Status | Evidence |
44
+ | --- | --- | --- |
45
+ | Project uses at least one automated test suite | Met | Vitest: unit, integration, e2e, perf projects |
46
+ | Project has FLOSS automated test suite invoked on all commits/PRs | Met | `ci.yml` |
47
+ | New functionality has accompanying tests (documented expectation) | Met | `CONTRIBUTING.md` PR checklist requires it |
48
+ | Project has a general policy for test coverage | Met | `vitest.config.ts` enforces 85–90% thresholds |
49
+ | Project uses continuous integration | Met | `ci.yml` on push/PR/schedule |
50
+ | Project has at least one static analysis tool applied | Met | CodeQL, ESLint, mutation testing (Stryker) |
51
+ | All medium/high-severity static analysis findings are addressed | Met (2026-07-03) | All 8 CodeQL alerts closed: 1 fixed in code, 7 dismissed with rationale re-verified against current code — see `SECURITY_DECISIONS.md`. (Separately, Trivy container-image scanning added the same day found 50+ new findings against the built Docker image, mostly in the base image's bundled tooling rather than this project's source — tracked as an open item in `docs/repo-maturity-report.md`, not part of this specific criterion, which concerns source static analysis) |
52
+
53
+ ## Security
54
+
55
+ | Criterion | Status | Evidence |
56
+ | --- | --- | --- |
57
+ | Project has a documented security architecture / defaults | Met | SECURITY_DECISIONS.md, README "Security Defaults" |
58
+ | Cryptographic functions use public, well-reviewed protocols | Met | `jose` for JWT/OAuth verification (per `package.json` deps); no bespoke crypto identified |
59
+ | Secrets are not stored in the repository | Met (best-effort) | GitHub secret scanning + push protection enabled; `gitleaks.yml` added by this PR for defense-in-depth history scanning |
60
+ | Delivery mechanisms protect against MITM | Met | npm publish over registry.npmjs.org HTTPS with npm provenance; container images pinned by digest per `docs/docker.md` |
61
+
62
+ ## Analysis
63
+
64
+ | Criterion | Status | Evidence |
65
+ | --- | --- | --- |
66
+ | Static analysis applied to any language used | Met | CodeQL (JS/TS query pack) |
67
+ | Dynamic analysis applied | Partially met | Mutation testing (Stryker) exercises runtime behavior of security-critical code paths; no fuzzing harness exists, and none is currently believed necessary (see repo-maturity-report.md, "Fuzzing: Not applicable") |
68
+
69
+ ## Bottom line
70
+
71
+ As of 2026-07-03, the CodeQL blocker has been resolved. The remaining blocker is
72
+ "cut a first release," already tracked in [ROADMAP.md](../ROADMAP.md), plus triaging
73
+ the new Trivy container-image findings noted above. Nothing in this evidence log claims
74
+ Silver or Gold; see [docs/openssf-gap-analysis.md](openssf-gap-analysis.md) for why
75
+ those are out of reach right now.
@@ -0,0 +1,63 @@
1
+ # OpenSSF Gap Analysis
2
+
3
+ This document separates what's achievable now (Passing) from what genuinely requires
4
+ organizational change (Silver, Gold), so that neither this audit nor future readers
5
+ overclaim.
6
+
7
+ ## Passing — achievable now
8
+
9
+ See [docs/openssf-evidence.md](openssf-evidence.md) for the full criterion table.
10
+
11
+ ~~Seven open CodeQL alerts (one High) with no fully-current documented disposition.~~
12
+ **Resolved 2026-07-03** — see `SECURITY_DECISIONS.md`.
13
+
14
+ The remaining concrete blocker: no release has been cut yet, so "release notes exist
15
+ for each release" is unverified in practice even though the mechanism (release-please +
16
+ CHANGELOG) is implemented. Tracked in [ROADMAP.md](../ROADMAP.md) and as a recommended
17
+ issue in [docs/repo-maturity-report.md](repo-maturity-report.md).
18
+
19
+ A new, unrelated item surfaced the same day: Trivy container-image scanning (added
20
+ after this document was first written) found 50+ open findings against the built
21
+ Docker image — see "Package publishing maturity" in
22
+ [docs/repo-maturity-report.md](repo-maturity-report.md). This doesn't block Passing
23
+ (that criterion concerns source static analysis, not container base-image CVEs) but is
24
+ worth tracking as its own item.
25
+
26
+ ## Silver — not yet achievable
27
+
28
+ OpenSSF Best Practices Silver adds requirements this project cannot honestly claim yet,
29
+ specifically:
30
+
31
+ - **Two or more unassociated significant contributors.** Current bus factor is 1
32
+ (single collaborator, `oaslananka`). Silver requires evidence of contribution from
33
+ people not all affiliated with the same entity.
34
+ - **A documented process for reviewing and responding to bug reports within a
35
+ well-defined window, evidenced by actual history.** The process is documented
36
+ (`SECURITY.md`, `SUPPORT.md`), but there is no report history yet to evidence it.
37
+ - **Basic floor for cryptographic practices is fully documented** (partially met —
38
+ `SECURITY_DECISIONS.md` covers several defaults but doesn't exhaustively enumerate
39
+ every cryptographic operation in the codebase).
40
+
41
+ **What would need to change:** a second active contributor with merged, substantive
42
+ PRs; at least one real vulnerability report handled end-to-end; and a pass over
43
+ `SECURITY_DECISIONS.md` to make the cryptographic inventory exhaustive.
44
+
45
+ ## Gold — explicitly out of scope for a claim today
46
+
47
+ Per the audit's own instructions, Gold is only evaluated as a gap list when its
48
+ preconditions are absent — and they are absent here:
49
+
50
+ | Gold precondition | Current state |
51
+ | --- | --- |
52
+ | Multiple active maintainers | 1 (see MAINTAINERS.md) |
53
+ | Independent contributor/reviewer | 0 non-bot PRs have ever been opened |
54
+ | Regular human PR review | 0 human-reviewed PRs to date |
55
+ | Branch protection | Not applied on GitHub (ruleset JSON exists but unapplied) |
56
+ | Sustainable governance | Solo-maintainer model, honestly documented in GOVERNANCE.md, not yet a multi-party governance structure |
57
+ | High test coverage sustained over time | Coverage thresholds exist (85–90%) but "sustained over time" needs release/version history that doesn't exist yet (repo is 5 days old) |
58
+ | Repeatable/reproducible release process | Implemented (SBOM, checksums, provenance) but never executed — zero releases published |
59
+
60
+ **This is a gap list, not a target with a date.** None of these should be worked toward
61
+ by editing files; they require either time (release/contribution history accruing
62
+ naturally) or a maintainer decision (branch protection, recruiting a co-maintainer).
63
+ Revisit this analysis after the "Silver" preconditions above are met.
@@ -0,0 +1,37 @@
1
+ # OpenSSF Proposal Links
2
+
3
+ Reference links for acting on the findings in
4
+ [docs/repo-maturity-report.md](repo-maturity-report.md) and
5
+ [docs/openssf-gap-analysis.md](openssf-gap-analysis.md). These are pointers to where a
6
+ maintainer takes the next action, not automated submissions — no badge or scorecard
7
+ submission has been made on the project's behalf by this audit.
8
+
9
+ ## OpenSSF Best Practices badge
10
+
11
+ - Self-assessment form (submit once the CodeQL/first-release gaps in
12
+ `docs/openssf-gap-analysis.md` close): <https://www.bestpractices.dev/en/projects/new>
13
+ - Full criteria reference used for this audit: <https://www.bestpractices.dev/en/criteria/0>
14
+ - `.bestpractices.json` in the repo root tracks current self-assessment answers to make
15
+ filling out the form above mechanical.
16
+
17
+ ## OpenSSF Scorecard
18
+
19
+ - Public checks reference: <https://github.com/ossf/scorecard/blob/main/docs/checks.md>
20
+ - Scorecard viewer for this repo (once `publish_results` is re-enabled or a manual run
21
+ is published): <https://securityscorecards.dev/viewer/?uri=github.com/oaslananka/ssh-mcp-pro>
22
+ - `ossf/scorecard-action` documentation for the `publish_results` requirements that are
23
+ currently blocking automatic publication: <https://github.com/ossf/scorecard-action#authenticated-workflow>
24
+
25
+ ## GitHub repository configuration referenced by this audit
26
+
27
+ - Branch protection settings: `https://github.com/oaslananka/ssh-mcp-pro/settings/branches`
28
+ - Rulesets (to import `.github/rulesets/main-protection.json`):
29
+ `https://github.com/oaslananka/ssh-mcp-pro/settings/rules`
30
+ - Security & analysis settings (private vulnerability reporting, Dependabot security
31
+ updates): `https://github.com/oaslananka/ssh-mcp-pro/settings/security_analysis`
32
+ - Code scanning alerts: `https://github.com/oaslananka/ssh-mcp-pro/security/code-scanning`
33
+
34
+ ## CHAOSS
35
+
36
+ - Metrics reference used for the Community maturity section:
37
+ <https://chaoss.community/metrics/>
@@ -0,0 +1,81 @@
1
+ # Professionalization Plan
2
+
3
+ A phased plan synthesizing [docs/repo-maturity-report.md](repo-maturity-report.md)'s
4
+ "Safe refactor opportunities," "High-risk refactor opportunities," and "Recommended
5
+ issues" into an ordered sequence. This is a plan, not a commitment with dates — see
6
+ [ROADMAP.md](../ROADMAP.md) for the roadmap framing of the same underlying gaps.
7
+
8
+ ## Phase 0 — Done (this audit)
9
+
10
+ Documentation, governance, and evidence gaps that required no maintainer decision and
11
+ no behavior change: `CODE_OF_CONDUCT.md`, `GOVERNANCE.md`, `MAINTAINERS.md`,
12
+ `ROADMAP.md`, OpenSSF evidence/gap-analysis docs, Diátaxis documentation structure,
13
+ development-process docs, security docs, and the `gitleaks.yml` workflow.
14
+
15
+ ## Phase 1 — Maintainer decisions, no code change
16
+
17
+ Status as of 2026-07-03:
18
+
19
+ 1. Apply branch protection on `main` (import the existing ruleset or configure
20
+ equivalently). **Still open.**
21
+ 2. ~~Confirm private vulnerability reporting is enabled.~~ **Done** — confirmed
22
+ already enabled via API.
23
+ 3. ~~Decide on the Dependabot security-updates toggle.~~ **Done** — enabled, along
24
+ with Dependabot vulnerability alerts.
25
+ 4. Merge or re-trigger release-please PR #1 to ship the first release. **Still open**
26
+ — deliberately left for the maintainer; triggers a real publish action.
27
+ 5. Register the repo/workflow as an npm trusted publisher on npmjs.com (the workflow
28
+ side is already implemented; this is the registry-side counterpart). **Still open**
29
+ — an npmjs.com-side action outside the repo.
30
+
31
+ **Why this order:** branch protection and the first release are the two remaining
32
+ changes that would flip the most `Missing`/`Partial` rows in the maturity report to
33
+ `Passed` — they have outsized leverage relative to effort.
34
+
35
+ ## Phase 2 — Low-risk CI additions
36
+
37
+ Status as of 2026-07-03:
38
+
39
+ 1. ~~Container image vulnerability scanning (Trivy or Grype) in `docker.yml`.~~
40
+ **Done** — Trivy added, advisory-only. It found real findings: see the new Phase 3
41
+ item below.
42
+ 2. Hadolint for Dockerfile linting. **Still open.**
43
+ 3. ~~`publint` as an advisory (non-blocking) npm packaging check.~~ **Done** — added,
44
+ passes cleanly against the built package.
45
+
46
+ ## Phase 3 — Security follow-through
47
+
48
+ Status as of 2026-07-03:
49
+
50
+ 1. ~~Fix CodeQL alert #1 (High: clear-text logging in `scripts/start-chatgpt-http.mjs`).~~
51
+ **Done.**
52
+ 2. ~~Review and record disposition for the remaining 7 CodeQL alerts.~~ **Done** — all
53
+ dismissed via the code-scanning API with rationale re-verified against current code;
54
+ written up in `SECURITY_DECISIONS.md`.
55
+ 3. Re-evaluate Scorecard `publish_results: false` — either fix the blocking
56
+ `env:`/`defaults:` patterns across workflows, or adjust the README badge. **Still
57
+ open** — investigated; relocating those blocks across all 7 workflows risks
58
+ silently breaking the Windows Integration job's shell default, which can't be
59
+ verified without a live Windows Actions run.
60
+ 4. **New: triage the Trivy container-image backlog.** Adding Trivy in item 1 above
61
+ surfaced 50+ open findings against the built image — mostly the Alpine base image's
62
+ OpenSSL packages and npm/corepack's own bundled dependencies, not this project's own
63
+ code. Options: rebuild on a newer/slimmer base image, prune build tooling from the
64
+ final layer, or accept and document the current baseline explicitly.
65
+
66
+ ## Phase 4 — Community growth (not schedulable)
67
+
68
+ 1. Land a first externally-authored, human-reviewed pull request.
69
+ 2. Reconsider a second maintainer once Phase 4.1 has happened more than once — see
70
+ [GOVERNANCE.md](../GOVERNANCE.md#path-to-adding-maintainers).
71
+ 3. Revisit OpenSSF Silver readiness once Phase 4.1–4.2 give real evidence to point to.
72
+
73
+ ## What's explicitly not in this plan
74
+
75
+ - No plan to pursue OpenSSF Gold/foundation-grade on any timeline — see
76
+ [docs/openssf-gap-analysis.md](openssf-gap-analysis.md) for why that's an
77
+ organizational precondition problem, not a checklist.
78
+ - No plan to add PyPI, Go, or Rust packaging — none of those ecosystems are used by
79
+ this project.
80
+ - No plan to add a `.devcontainer` — assessed as optional given the toolchain is
81
+ already a single `pnpm install` away from working.
@@ -0,0 +1,21 @@
1
+ # Reference
2
+
3
+ Reference material is information-oriented: precise, exhaustive where practical, and
4
+ organized for lookup rather than reading start-to-finish. This index follows the
5
+ [Diátaxis](https://diataxis.fr/reference/) definition of "reference."
6
+
7
+ - [Environment variables](../../README.md#configuration) — the canonical
8
+ `SSH_MCP_*` configuration table lives in the README so it stays next to the
9
+ install/quickstart instructions; this reference doesn't duplicate it.
10
+ - [Tool profiles](../../README.md#tool-profiles) — which tools/resources/prompts each
11
+ connector profile (`full`, `remote-safe`, `chatgpt`, `claude`, `remote-readonly`,
12
+ `remote-broker`) exposes.
13
+ - [CLI reference](cli.md) — `ssh-mcp-pro` and `ssh-mcp-pro-agent` command-line flags.
14
+ - [Configuration reference](configuration.md) — the `SSH_MCP_*` table grouped by
15
+ concern, pointing back to the canonical table in the README.
16
+ - [Compatibility reference](compatibility.md) — supported Node.js/pnpm/OS/container
17
+ platforms and MCP transport/client compatibility.
18
+ - [MCP metadata](../../mcp.json) and [server metadata](../../server.json) — machine-readable
19
+ capability and registry declarations.
20
+ - [Security defaults](../../SECURITY_DECISIONS.md) — the exact default-deny behaviors and
21
+ why each exists.
@@ -0,0 +1,41 @@
1
+ # CLI Reference
2
+
3
+ ssh-mcp-pro installs two binaries (see `bin` in [package.json](../../package.json)):
4
+
5
+ - `ssh-mcp-pro` — the MCP server itself.
6
+ - `ssh-mcp-pro-agent` — the remote control-plane agent (see
7
+ [docs/remote-mcp-hardening.md](../remote-mcp-hardening.md)).
8
+
9
+ ## `ssh-mcp-pro`
10
+
11
+ Source of truth: [src/cli.ts](../../src/cli.ts).
12
+
13
+ | Flag / subcommand | Effect |
14
+ | --- | --- |
15
+ | (no args) | Starts the server over stdio transport (default) |
16
+ | `stdio` | Explicitly selects stdio transport |
17
+ | `http` | Selects the Streamable HTTP transport |
18
+ | `agent [...]` | Delegates all remaining arguments to the agent CLI (see below); consumes the rest of `argv` |
19
+ | `--help`, `-h` | Print help and exit |
20
+ | `--version`, `-v` | Print version and exit |
21
+ | `--stdio` | Forces stdio transport |
22
+ | `--transport=stdio` \| `--transport=http` | Explicit transport selection |
23
+ | `--host <value>` | HTTP bind host (overrides `SSH_MCP_HTTP_HOST`) |
24
+ | `--port <value>` | HTTP bind port (overrides `SSH_MCP_HTTP_PORT`) |
25
+ | `--bearer-token-file <path>` | Bearer token file for HTTP auth (overrides `SSH_MCP_HTTP_BEARER_TOKEN_FILE`) |
26
+ | `--enable-legacy-sse` | Enables legacy SSE compatibility (overrides `SSH_MCP_ENABLE_LEGACY_SSE`) |
27
+ | `--tool-profile <name>` | Selects a tool exposure profile (overrides `SSH_MCP_TOOL_PROFILE`) — see [Tool Profiles](../../README.md#tool-profiles) |
28
+ | `--connector-credential-provider <name>` | Selects a connector credential provider (overrides `SSH_MCP_CONNECTOR_CREDENTIAL_PROVIDER`) |
29
+ | `--no-stdio` | Unsupported/internal flag; do not rely on it |
30
+
31
+ Unknown flags are ignored rather than rejected, so that MCP clients invoking the binary
32
+ with extra arguments don't break the server.
33
+
34
+ ## `ssh-mcp-pro-agent`
35
+
36
+ Delegates directly to `src/remote/agent-cli.ts` via `argv` passthrough. See
37
+ [docs/remote-mcp-hardening.md](../remote-mcp-hardening.md) and
38
+ [docs/adding-a-device.md](../adding-a-device.md) for the documented agent workflows —
39
+ this reference intentionally doesn't restate flags that aren't yet enumerated in one
40
+ place in the source, to avoid documenting something that could drift silently from the
41
+ implementation.
@@ -0,0 +1,46 @@
1
+ # Compatibility Reference
2
+
3
+ ## Node.js
4
+
5
+ | Requirement | Value | Source |
6
+ | --- | --- | --- |
7
+ | Minimum supported range | `>=22.22.2 \|\| >=24.15.0 \|\| >=26.3.0` | `package.json` `engines.node` |
8
+ | CI matrix | Node 22, 24, 26 (`Unit Tests (Node 22/24/26)`) | `.github/workflows/ci.yml` |
9
+ | Local development / release builds | Node 24.15.0 / 26.3.0 | `CONTRIBUTING.md`, `release.yml` |
10
+
11
+ Note the gap between 22.22.2 and 24.15.0: Node 23.x is not supported (it was never an
12
+ LTS line). This is expected, not an oversight.
13
+
14
+ ## Package manager
15
+
16
+ pnpm `11.28.5`, enforced via `packageManager` in `package.json` and `engine-strict=true`.
17
+ npm/yarn are not supported installation paths for *developing* this repo (consumers
18
+ installing the published package via npm/npx are unaffected — see
19
+ [README.md#installation](../../README.md#installation)).
20
+
21
+ ## Operating systems
22
+
23
+ | OS | Status | Evidence |
24
+ | --- | --- | --- |
25
+ | Linux | Passed | Primary CI target; SSH fixture-based integration/E2E tests |
26
+ | Windows command paths | Passed | Dedicated `test:integration:windows` project on `windows-2022`; the `Windows Command-Path Integration` job validates local PowerShell and package-manager dispatch, not a real Windows OpenSSH server |
27
+ | macOS | Needs human confirmation | Listed as a supported platform in `mcp.json` (`platforms: [linux, macos, windows]`) but no macOS-specific CI job was found — likely covered incidentally by the cross-platform Node.js/SSH code paths, not by a dedicated test |
28
+
29
+ ## Container platforms
30
+
31
+ `linux/amd64`, `linux/arm64` — built and verified in `docker.yml` via Buildx + QEMU.
32
+
33
+ ## MCP transport compatibility
34
+
35
+ | Transport | Status |
36
+ | --- | --- |
37
+ | stdio | Passed — default, used by all documented client configs |
38
+ | Streamable HTTP | Passed — `http`/`--transport=http`, with the non-loopback safety gate described in [SECURITY_DECISIONS.md](../../SECURITY_DECISIONS.md) |
39
+ | Legacy SSE | Partial — available behind `SSH_MCP_ENABLE_LEGACY_SSE` for compatibility, not the recommended path |
40
+
41
+ ## MCP client compatibility
42
+
43
+ Documented configs exist for a generic stdio client, VS Code, and Claude Desktop (see
44
+ [README.md#quickstart](../../README.md#quickstart)) plus connector-shaped profiles for
45
+ ChatGPT and Claude remote connectors (`chatgpt`, `claude` tool profiles). See
46
+ [Tool Profiles](../../README.md#tool-profiles) for exactly what each profile exposes.