ssh-mcp-pro 1.1.5 → 1.2.1

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 (123) hide show
  1. package/CHANGELOG.md +29 -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 +3 -0
  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.map +1 -1
  32. package/dist/prompts.js +8 -1
  33. package/dist/prompts.js.map +1 -1
  34. package/dist/remote/agent-cli.d.ts.map +1 -1
  35. package/dist/remote/agent-cli.js +20 -1
  36. package/dist/remote/agent-cli.js.map +1 -1
  37. package/dist/remote/agent-connection-lifecycle.d.ts +47 -0
  38. package/dist/remote/agent-connection-lifecycle.d.ts.map +1 -0
  39. package/dist/remote/agent-connection-lifecycle.js +304 -0
  40. package/dist/remote/agent-connection-lifecycle.js.map +1 -0
  41. package/dist/remote/agent-executor.d.ts.map +1 -1
  42. package/dist/remote/agent-executor.js +93 -68
  43. package/dist/remote/agent-executor.js.map +1 -1
  44. package/dist/remote/agent-handler.d.ts +22 -8
  45. package/dist/remote/agent-handler.d.ts.map +1 -1
  46. package/dist/remote/agent-handler.js +151 -98
  47. package/dist/remote/agent-handler.js.map +1 -1
  48. package/dist/remote/config.d.ts.map +1 -1
  49. package/dist/remote/config.js +14 -0
  50. package/dist/remote/config.js.map +1 -1
  51. package/dist/remote/control-plane.d.ts.map +1 -1
  52. package/dist/remote/control-plane.js +3 -0
  53. package/dist/remote/control-plane.js.map +1 -1
  54. package/dist/remote/policy-update-guard.d.ts +14 -0
  55. package/dist/remote/policy-update-guard.d.ts.map +1 -0
  56. package/dist/remote/policy-update-guard.js +27 -0
  57. package/dist/remote/policy-update-guard.js.map +1 -0
  58. package/dist/remote/policy.d.ts.map +1 -1
  59. package/dist/remote/policy.js +13 -2
  60. package/dist/remote/policy.js.map +1 -1
  61. package/dist/remote/safe-file.d.ts +5 -0
  62. package/dist/remote/safe-file.d.ts.map +1 -0
  63. package/dist/remote/safe-file.js +194 -0
  64. package/dist/remote/safe-file.js.map +1 -0
  65. package/dist/remote/store.d.ts.map +1 -1
  66. package/dist/remote/store.js.map +1 -1
  67. package/dist/remote/types.d.ts +5 -0
  68. package/dist/remote/types.d.ts.map +1 -1
  69. package/dist/remote/websocket.d.ts +38 -13
  70. package/dist/remote/websocket.d.ts.map +1 -1
  71. package/dist/remote/websocket.js +90 -126
  72. package/dist/remote/websocket.js.map +1 -1
  73. package/dist/render-http.js +2 -1
  74. package/dist/render-http.js.map +1 -1
  75. package/dist/retry.d.ts.map +1 -1
  76. package/dist/retry.js +4 -2
  77. package/dist/retry.js.map +1 -1
  78. package/dist/server-http.d.ts +1 -1
  79. package/dist/server-http.d.ts.map +1 -1
  80. package/dist/server-http.js +1 -421
  81. package/dist/server-http.js.map +1 -1
  82. package/dist/tools/connector.provider.d.ts.map +1 -1
  83. package/dist/tools/connector.provider.js +2 -1
  84. package/dist/tools/connector.provider.js.map +1 -1
  85. package/dist/types.d.ts.map +1 -1
  86. package/dist/types.js.map +1 -1
  87. package/docs/agent-runtime-config.md +34 -0
  88. package/docs/development/api-stability.md +44 -0
  89. package/docs/development/coding-standards.md +35 -0
  90. package/docs/development/commit-conventions.md +35 -0
  91. package/docs/development/dependency-management.md +60 -0
  92. package/docs/development/deprecation-policy.md +29 -0
  93. package/docs/development/local-setup.md +46 -0
  94. package/docs/development/release-process.md +57 -0
  95. package/docs/development/testing-policy.md +47 -0
  96. package/docs/explanation/architecture.md +44 -0
  97. package/docs/how-to/README.md +14 -0
  98. package/docs/how-to/configure-ssh-policy.md +50 -0
  99. package/docs/openssf-evidence.md +75 -0
  100. package/docs/openssf-gap-analysis.md +63 -0
  101. package/docs/openssf-proposal-links.md +37 -0
  102. package/docs/professionalization-plan.md +81 -0
  103. package/docs/reference/README.md +21 -0
  104. package/docs/reference/cli.md +41 -0
  105. package/docs/reference/compatibility.md +46 -0
  106. package/docs/reference/configuration.md +72 -0
  107. package/docs/remote-mcp-hardening.md +10 -0
  108. package/docs/repo-maturity-report.md +565 -0
  109. package/docs/security/assurance-case.md +58 -0
  110. package/docs/security/dependency-audit-exceptions.json +4 -0
  111. package/docs/security/dependency-audit-exceptions.md +15 -0
  112. package/docs/security/input-validation.md +54 -0
  113. package/docs/security/release-integrity.md +28 -0
  114. package/docs/security/scanning-policy.md +50 -0
  115. package/docs/security/secrets-management.md +49 -0
  116. package/docs/security/threat-model.md +65 -0
  117. package/docs/testing.md +24 -0
  118. package/docs/troubleshooting.md +56 -0
  119. package/docs/tutorials/getting-started.md +84 -0
  120. package/mcp.json +1 -1
  121. package/package.json +82 -68
  122. package/registry/ssh-mcp-pro/mcp.json +1 -1
  123. package/server.json +3 -3
@@ -0,0 +1,44 @@
1
+ # API Stability
2
+
3
+ This project has never made an explicit stability statement before this document. It's
4
+ written now to describe intent going forward, not to claim a history of enforcement
5
+ that doesn't exist — see [docs/repo-maturity-report.md](../repo-maturity-report.md)
6
+ ("API/CLI stability") for the audit that identified this gap.
7
+
8
+ ## What is considered "the public API"
9
+
10
+ For SemVer purposes, going forward:
11
+
12
+ 1. **MCP tool/resource/prompt schemas** exposed by the `full` profile — names, required
13
+ parameters, and result shapes. Covered by `test/unit/mcp-contract.test.ts`.
14
+ 2. **CLI flags** in `docs/reference/cli.md` (mirrors `src/cli.ts`).
15
+ 3. **`SSH_MCP_*` environment variables** and their default values.
16
+ 4. **The two published binaries' existence and basic invocation** (`ssh-mcp-pro`,
17
+ `ssh-mcp-pro-agent`).
18
+
19
+ ## What is explicitly not covered
20
+
21
+ - Internal module structure under `src/` (anything not re-exported).
22
+ - Exact wording of error messages and log lines.
23
+ - The shape of `SSH_MCP_POLICY_FILE` beyond what's documented — see the config-schema
24
+ gap noted in the maturity report; this will need its own versioning story before it
25
+ can be a stability guarantee.
26
+ - Tool profiles *other than* `full` gaining new tools without a version bump — adding a
27
+ tool to a restricted profile is additive from that profile's consumer's perspective,
28
+ but is still called out here as unresolved: **Needs human confirmation** on whether
29
+ profile membership changes should be treated as SemVer-significant.
30
+
31
+ ## What counts as breaking
32
+
33
+ Per Conventional Commits (`!` / `BREAKING CHANGE:` — see
34
+ [commit-conventions.md](commit-conventions.md)): removing or renaming a tool/flag/env
35
+ var, changing a tool's required parameters, changing a default value that changes
36
+ security posture (e.g. flipping a deny-by-default to allow-by-default), or removing a
37
+ supported Node.js version ahead of its own EOL.
38
+
39
+ ## Current honest caveat
40
+
41
+ The project is on `1.x` but is 5 days old with zero external consumers on record. This
42
+ policy is aspirational until it's been exercised against a real breaking change and a
43
+ real major version bump — that will be the first real test of whether this document
44
+ holds up in practice.
@@ -0,0 +1,35 @@
1
+ # Coding Standards
2
+
3
+ This is a reference index, not a new rulebook — every rule below is already enforced by
4
+ a config file or CI job. It exists so "what are the coding standards" has one answer
5
+ instead of requiring a tour of five config files.
6
+
7
+ | Concern | Enforced by | Command |
8
+ | --- | --- | --- |
9
+ | Formatting | [.prettierrc.json](../../.prettierrc.json), [.editorconfig](../../.editorconfig) | `pnpm run format:check` |
10
+ | Linting | [eslint.config.mjs](../../eslint.config.mjs) | `pnpm run lint` |
11
+ | Type safety | [tsconfig.json](../../tsconfig.json) (`strict` mode) | `pnpm run typecheck` |
12
+ | Module size | [scripts/check-module-size.mjs](../../scripts/check-module-size.mjs) | `pnpm run check:module-size` |
13
+ | Circular dependencies | [.dependency-cruiser.mjs](../../.dependency-cruiser.mjs) | `pnpm run check:circular` |
14
+ | Unused code/exports | [knip.jsonc](../../knip.jsonc) | `pnpm run check:knip` |
15
+ | Commit messages | [.commitlintrc.json](../../.commitlintrc.json) | see [commit-conventions.md](commit-conventions.md) |
16
+ | Documentation prose | [scripts/check-doc-language.mjs](../../scripts/check-doc-language.mjs) | `pnpm run check:doc-language` |
17
+
18
+ Run everything at once with `pnpm run check:maintainability` (module size + circular
19
+ deps + unused exports) or `pnpm run check:quality` (formatting, lint, typecheck, audit,
20
+ licenses, and more — see [CONTRIBUTING.md](../../CONTRIBUTING.md) for the full gate).
21
+
22
+ ## Security-sensitive code
23
+
24
+ Files covered by [.github/CODEOWNERS](../../.github/CODEOWNERS) (`src/safety.ts`,
25
+ `src/session.ts`, `src/logging.ts`, `src/tools/`) and the mutation-tested surfaces listed
26
+ in [testing-policy.md](testing-policy.md) receive extra scrutiny. Changes there should
27
+ be justified against [SECURITY_DECISIONS.md](../../SECURITY_DECISIONS.md), not just
28
+ against passing tests.
29
+
30
+ ## Pre-commit / pre-push hooks
31
+
32
+ [.pre-commit-config.yaml](../../.pre-commit-config.yaml) and the git hooks installed by
33
+ `scripts/setup-git-hooks.mjs` (run automatically via the `prepare` npm script) run a
34
+ subset of the above on staged files before commit, and the full `check:push` gate before
35
+ push. See [CONTRIBUTING.md](../../CONTRIBUTING.md#git-hooks) for details.
@@ -0,0 +1,35 @@
1
+ # Commit Conventions
2
+
3
+ ssh-mcp-pro uses [Conventional Commits](https://www.conventionalcommits.org/), enforced
4
+ by [.commitlintrc.json](../../.commitlintrc.json) and
5
+ [scripts/lint-commits.mjs](../../scripts/lint-commits.mjs) (`pnpm run lint:commits`).
6
+ PR titles are additionally linted by
7
+ [scripts/lint-pr-title.mjs](../../scripts/lint-pr-title.mjs) (`pnpm run lint:pr-title`),
8
+ since release-please derives the changelog and version bump from these.
9
+
10
+ ## Format
11
+
12
+ ```
13
+ <type>[optional !][: ]<description>
14
+
15
+ [optional body]
16
+
17
+ [optional footer(s)]
18
+ ```
19
+
20
+ ## Types in use
21
+
22
+ `feat`, `fix`, `docs`, `test`, `refactor`, `build`, `ci`, `chore` — see
23
+ [CONTRIBUTING.md](../../CONTRIBUTING.md) for the authoritative list and examples.
24
+
25
+ ## Breaking changes
26
+
27
+ Mark with `!` after the type (e.g. `feat!:`) or a `BREAKING CHANGE:` footer.
28
+ Release-please uses this to trigger a major version bump and a distinct CHANGELOG
29
+ section — see [docs/development/release-process.md](release-process.md) and
30
+ [MIGRATION.md](../../MIGRATION.md).
31
+
32
+ ## Branch naming
33
+
34
+ `feature/*`, `fix/*`, `docs/*`, `chore/*` (and project-specific prefixes such as
35
+ `codex/SSH-*`) — see [CONTRIBUTING.md](../../CONTRIBUTING.md) for current conventions.
@@ -0,0 +1,60 @@
1
+ # Dependency Management
2
+
3
+ ## Two update bots, two ecosystems, by design
4
+
5
+ - **[renovate.json](../../renovate.json)** manages the `npm` ecosystem (production and
6
+ dev dependencies). Patch/minor devDependency updates automerge; the MCP SDK
7
+ (`@modelcontextprotocol/sdk`) is explicitly excluded from automerge given how central
8
+ it is; typescript-eslint packages are grouped.
9
+ - **[.github/dependabot.yml](../../.github/dependabot.yml)** manages the
10
+ `github-actions` ecosystem only, weekly, capped at 5 open PRs.
11
+
12
+ npm dependencies are deliberately **not** duplicated into `dependabot.yml` — running two
13
+ bots against the same ecosystem tends to produce competing PRs for the same bump. If
14
+ you're looking for why Dependabot doesn't also open npm PRs, this is why; it's a design
15
+ choice, not an oversight (see the corresponding row in
16
+ [docs/repo-maturity-report.md](../repo-maturity-report.md)).
17
+
18
+ ## Vulnerability response
19
+
20
+ - `pnpm audit --audit-level moderate` (the `audit` script) runs as part of
21
+ `check:quality`.
22
+ - The `Dependency Review` job in [ci.yml](../../.github/workflows/ci.yml) is the single
23
+ canonical dependency review for pull requests. It uses
24
+ `dependency-review-action` with `fail-on-severity: moderate`, so a newly introduced
25
+ advisory rated moderate, high, or critical blocks the pull request. A second standalone
26
+ dependency-review workflow is intentionally not used because duplicate checks can apply
27
+ contradictory thresholds and create ambiguous merge signals.
28
+ - The same job enforces the repository license allowlist. The allowlist is kept identical
29
+ to [scripts/check-licenses.mjs](../../scripts/check-licenses.mjs) by a regression test;
30
+ changing either policy requires a reviewed pull request that updates both surfaces.
31
+ - GitHub Dependabot alerts (vulnerability scanning, distinct from the version-update
32
+ bot config above) apply automatically on a public GitHub repository. Whether
33
+ "Dependabot security updates" (automatic fix PRs) is enabled is a repository Settings
34
+ toggle — see the manual actions list in the maturity report.
35
+
36
+ ### Published artifact verification
37
+
38
+ `pnpm run audit:packed` installs the generated npm tarball in a clean consumer project
39
+ and audits the resolved production graph without inheriting workspace overrides. Any
40
+ remaining finding must match the machine-readable, time-bounded policy in
41
+ [dependency-audit-exceptions.json](../security/dependency-audit-exceptions.json) and the
42
+ public rationale in [Dependency Audit Exceptions](../security/dependency-audit-exceptions.md).
43
+ Unexpected, expired, or stale exceptions fail the package gate.
44
+
45
+ ## License compliance
46
+
47
+ [scripts/check-licenses.mjs](../../scripts/check-licenses.mjs) enforces an allowlist
48
+ (MIT, Apache-2.0, BSD-2/3-Clause, BlueOak-1.0.0, CC-\*, ISC, Python-2.0, Unlicense)
49
+ against every resolved dependency, run via `pnpm run licenses:check` as part of
50
+ `check:quality`.
51
+
52
+ ## Freshness (advisory)
53
+
54
+ `pnpm run check:freshness` (`scripts/check-dependency-freshness.mjs`) compares the
55
+ pinned Node.js floor, pnpm version, and dependency versions against upstream metadata,
56
+ producing `artifacts/dependency-freshness.{json,md}`. It's advisory unless a pinned
57
+ version is unsupported, EOL, deprecated, or vulnerable — see
58
+ [CONTRIBUTING.md](../../CONTRIBUTING.md) for the exact policy and
59
+ [docs/audit/2026-06-05-ecosystem-audit.md](../audit/2026-06-05-ecosystem-audit.md) for
60
+ the most recent full audit.
@@ -0,0 +1,29 @@
1
+ # Deprecation Policy
2
+
3
+ No formal deprecation has ever happened in this project's history (it's 5 days old at
4
+ time of writing), so this document states intent, not a track record — consistent with
5
+ [docs/development/api-stability.md](api-stability.md).
6
+
7
+ ## Policy going forward
8
+
9
+ 1. **Announce before removing.** A deprecated tool, flag, or environment variable is
10
+ marked as such in `CHANGELOG.md` and, where feasible, the tool/flag continues to
11
+ function with a warning for at least one minor release before removal.
12
+ 2. **Removal is a breaking change.** Per
13
+ [docs/development/api-stability.md](api-stability.md), actual removal happens in a
14
+ major version bump, using Conventional Commits' `!`/`BREAKING CHANGE:` convention.
15
+ 3. **Security-driven deprecation is the exception.** If a default or feature is found
16
+ to be actively unsafe (not just superseded), it may be deprecated and disabled faster
17
+ than the normal window, with the rationale documented in
18
+ [SECURITY_DECISIONS.md](../../SECURITY_DECISIONS.md) — safety takes priority over the
19
+ standard notice period.
20
+ 4. **`MIGRATION.md` is the landing page** for any deprecation that requires action from
21
+ users upgrading between versions.
22
+
23
+ ## What this policy does not yet cover
24
+
25
+ - A specific minimum notice period (e.g. "one minor version" vs. "90 days") — left
26
+ unspecified until there's a real deprecation to calibrate against.
27
+ - Deprecation of MCP tool schema fields specifically (as opposed to whole tools) — see
28
+ the open question in [docs/development/api-stability.md](api-stability.md) about
29
+ whether profile/schema changes are SemVer-significant.
@@ -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,57 @@
1
+ # Release Process
2
+
3
+ ## Versioning
4
+
5
+ Semantic Versioning, automated by [release-please](https://github.com/googleapis/release-please)
6
+ per [release-please-config.json](../../release-please-config.json) (`node` release
7
+ type). Conventional Commit types on `main` (see
8
+ [commit-conventions.md](commit-conventions.md)) drive whether the next release is a
9
+ patch, minor, or major bump.
10
+
11
+ ## How a release happens
12
+
13
+ 1. Conventional commits land on `main`.
14
+ 2. The `release` job in [.github/workflows/release.yml](../../.github/workflows/release.yml)
15
+ runs `googleapis/release-please-action`, which opens or updates a release PR
16
+ (updating `CHANGELOG.md`, `.release-please-manifest.json`, and the version fields
17
+ release-please is configured to touch — see `extra-files` in
18
+ `release-please-config.json`, which includes `mcp.json`, `server.json`, and the MCP
19
+ registry record).
20
+ 3. Merging that PR triggers `release_created`, which runs the `release-assets` job:
21
+ - `pnpm run check` (full quality gate) must pass.
22
+ - `pnpm run sbom` generates a CycloneDX SBOM.
23
+ - The package is packed with `pnpm pack`, and both the tarball and the SBOM get a
24
+ `sha256sum` checksum file.
25
+ - `actions/attest-build-provenance` runs twice — once for the package, once for the
26
+ SBOM — producing verifiable build provenance attestations.
27
+ - All artifacts (tarball, SBOM, checksums) are attached to the GitHub Release.
28
+ - npm publish only happens if the repository variable `AUTO_RELEASE_PUBLISH` is
29
+ `"true"`; otherwise the step is a documented no-op ("npm publishing is disabled by
30
+ AUTO_RELEASE_PUBLISH").
31
+
32
+ ## Verifying a release artifact
33
+
34
+ ```bash
35
+ sha256sum -c ssh-mcp-pro-<version>.tgz.sha256
36
+ ```
37
+
38
+ For provenance, use [GitHub CLI attestation verification](https://cli.github.com/manual/gh_attestation_verify):
39
+
40
+ ```bash
41
+ gh attestation verify ssh-mcp-pro-<version>.tgz --owner oaslananka
42
+ ```
43
+
44
+ See [docs/security/release-integrity.md](../security/release-integrity.md) for the full
45
+ supply-chain rationale.
46
+
47
+ ## Current status
48
+
49
+ No release has been published yet (repository created 2026-06-28). Release PR #1
50
+ (`chore(main): release ssh-mcp-pro 1.2.0`) is open and is the first opportunity to
51
+ exercise this pipeline end-to-end — see [ROADMAP.md](../../ROADMAP.md).
52
+
53
+ ## Container images
54
+
55
+ Published separately by [.github/workflows/docker.yml](../../.github/workflows/docker.yml)
56
+ on tagged releases; see [docs/docker.md](../docker.md) for tag policy and digest-pinned
57
+ usage.
@@ -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/>