Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.

Add new ssh-mitm driver for jumpstarter#768

Merged
mangelajo merged 3 commits intojumpstarter-dev:mainfrom
bkhizgiy:ssh
Dec 7, 2025
Merged

Add new ssh-mitm driver for jumpstarter#768
mangelajo merged 3 commits intojumpstarter-dev:mainfrom
bkhizgiy:ssh

Conversation

@bkhizgiy
Copy link
Copy Markdown
Member

@bkhizgiy bkhizgiy commented Dec 4, 2025

Adds a new SSH MITM driver that keeps SSH private keys securely on the exporter.
Implements a network layer driver (SSHMITM) that acts as a secure SSH proxy between
clients and DUTs, designed to be used as a child of SSHWrapper:

Architecture: SSHWrapper → SSHMITM → TcpNetwork → DUT

  • SSHWrapper handles SSH CLI and command execution (runs local SSH binary)
  • SSHMITM provides authenticated proxy connection using Paramiko
  • TcpNetwork provides raw TCP connection to the DUT

Usage: Configure as child of SSHWrapper, then use standard SSH commands:
j ssh_mitm <cmd -arg1 -arg2> # Execute command
j ssh_mitm # Interactive shell

Summary by CodeRabbit

  • New Features

    • Added SSH Man-in-the-Middle (MITM) driver to proxy SSH sessions, with configurable target network and identity options (inline key or file).
    • Included an example YAML configuration for driver setup.
  • Documentation

    • Added comprehensive README covering installation, configuration, examples, and usage.
  • Tests

    • Added extensive unit and async tests validating configuration, key handling, connection flow, and proxy behavior.
  • Chores

    • Added project packaging/configuration for the new driver.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 4, 2025

Walkthrough

Adds a new SSH MITM driver package with an SSHMITM driver implementation, example exporter YAML, tests, packaging (pyproject.toml), and README; the driver runs an ephemeral SSH server, relays client sessions to a TCP target via Paramiko, and exposes an async stream connect endpoint.

Changes

Cohort / File(s) Summary
Core SSH MITM Driver
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py
New SSHMITM driver class implementing an ephemeral SSH server, identity resolution (inline or file), private key loading, DUT SSH client creation, channel/session handling, bidirectional proxying, MITMServerInterface for SSH negotiation, async connect() stream endpoint, and cleanup methods.
Tests
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py
New unit tests covering configuration validation, inline/file identity handling, key loading (Ed25519/RSA/fallback/unsupported), stream/connect behavior, session proxying, timeouts, and resource cleanup using mocks/patches.
Project Configuration
packages/jumpstarter-driver-ssh-mitm/pyproject.toml
New package metadata, dependencies (paramiko, jumpstarter, jumpstarter-driver-network, anyio), entry points, hatch versioning and build hooks, test configuration and dev dependencies.
Documentation & Example
packages/jumpstarter-driver-ssh-mitm/README.md, packages/jumpstarter-driver-ssh-mitm/examples/exporter.yaml
Added README with installation/usage/configuration and an exporter YAML example demonstrating SSHMITM config, inline/file identity options, tcp child wiring and sample host/port.

Sequence Diagram

sequenceDiagram
    actor Client as SSH Client
    participant MITM as SSHMITM (SSH Server)
    participant DUT as Remote Target (DUT)

    Client->>MITM: SSH Connect + Auth (client creds)
    activate MITM
    MITM->>MITM: MITMServerInterface negotiates session, records username
    MITM->>DUT: Create SSH client -> Connect (host, port, identity)
    activate DUT
    DUT-->>MITM: SSH Ready / Auth OK
    MITM-->>Client: Complete Auth / Session OK

    alt Shell Session
        Client->>MITM: Request Shell / PTY
        MITM->>DUT: Open shell channel
        MITM->>MITM: _proxy_channels() starts bidirectional threads
    else Exec Command
        Client->>MITM: Exec request
        MITM->>DUT: Exec on DUT
        MITM->>MITM: _proxy_channels() starts bidirectional threads
    end

    rect rgb(220,240,255)
    Note over Client,DUT: Data proxied in separate daemon threads (stdin/stdout/stderr)
    Client<<->>MITM: proxied streams
    MITM<<->>DUT: proxied streams
    end

    Client->>MITM: Close Session
    MITM->>DUT: Close channel & transport
    deactivate DUT
    MITM->>MITM: Cleanup transports, keys, threads
    deactivate MITM
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • driver.py: Paramiko integration, MITMServerInterface behavior, thread-safety, stream-to-transport bridging, and cleanup.
  • Identity/key handling: file read error paths, key-type detection and fallback.
  • Tests: ensure mocks/async coverage accurately reflect real network behavior and edge cases.
  • Packaging: verify pyproject tooling and entry points.

Suggested Reviewers

  • mangelajo
  • kirkbrauer
  • NickCao

Poem

🐇 I hop a tunnel, keys aglow and spry,

I bridge the client to far-off sky,
Threads hum softly, channels sing,
Ephemeral keys in the midnight spring,
A rabbit cheers — proxy on high! 🎩✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 63.93% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add new ssh-mitm driver for jumpstarter' accurately and concisely describes the main change: introduction of a new SSH MITM driver component.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@netlify
Copy link
Copy Markdown

netlify Bot commented Dec 4, 2025

Deploy Preview for jumpstarter-docs ready!

Name Link
🔨 Latest commit 08876ac
🔍 Latest deploy log https://app.netlify.com/projects/jumpstarter-docs/deploys/6935c97df3e91a0008765d47
😎 Deploy Preview https://deploy-preview-768--jumpstarter-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@bkhizgiy bkhizgiy requested a review from mangelajo December 4, 2025 08:23
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (4)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py (2)

184-199: Security: Host key checking is disabled.

StrictHostKeyChecking=no and UserKnownHostsFile=/dev/null are appropriate here since the MITM proxy generates ephemeral host keys. Consider adding a comment explaining this is intentional for the MITM use case.

             ssh_command = [
                 ssh_binary,
                 "-p",
                 str(port),
+                # Disable host key checking - the MITM proxy generates ephemeral keys
                 "-o",
                 "StrictHostKeyChecking=no",
                 "-o",
                 "UserKnownHostsFile=/dev/null",
                 f"{username}@{host}",
             ]

282-298: Port forwarding loop could use a more graceful shutdown mechanism.

The while True: time.sleep(1) pattern works but consumes unnecessary CPU cycles. Consider using an event-based wait.

     def _start_forward(self, local_host: str, local_port: int):
         """Expose the SSH MITM server on a local TCP port."""
         click.echo("Starting local forward (Ctrl+C to stop)...")
         try:
             with TcpPortforwardAdapter(
                 client=self,
                 method="connect",
                 local_host=local_host,
                 local_port=local_port,
             ) as (bound_host, bound_port):
                 click.echo(f"Local endpoint: {bound_host}:{bound_port}")
                 click.echo(f"Example: ssh -p {bound_port} localhost")
                 click.echo("Press Ctrl+C to stop forwarding.")
-                while True:
-                    time.sleep(1)
+                import threading
+                stop_event = threading.Event()
+                stop_event.wait()
         except KeyboardInterrupt:
             click.echo("\nForward stopped.")

This is a minor optimization and the current approach is functional.

packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py (2)

37-40: Unusual import placement after logging configuration.

Imports after the logging.getLogger() call is unconventional. Consider moving all imports to the top of the file.

+from jumpstarter_driver_network.driver import TcpNetwork
+from jumpstarter_driver_ssh.driver import SSHWrapper
+
 import paramiko
 from anyio import get_cancelled_exc_class
 from anyio.from_thread import BlockingPortal
 from jumpstarter.common.exceptions import ConfigurationError
 from jumpstarter.driver import Driver, export, exportstream
 from jumpstarter.streams.common import create_memory_stream

 logging.getLogger("paramiko").setLevel(logging.WARNING)
-
-from jumpstarter_driver_network.driver import TcpNetwork
-from jumpstarter_driver_ssh.driver import SSHWrapper

252-253: Consider making host key size configurable or using a larger default.

While 2048-bit RSA is currently acceptable, NIST recommends transitioning to 3072-bit RSA for security through 2030. Since this is an ephemeral key, the performance impact of a larger key is minimal.

-        self._host_key = paramiko.RSAKey.generate(2048)
+        self._host_key = paramiko.RSAKey.generate(3072)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5300757 and 5a8b654.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • packages/jumpstarter-driver-ssh-mitm/examples/exporter.yaml (1 hunks)
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py (1 hunks)
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py (1 hunks)
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py (1 hunks)
  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml (1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
packages/jumpstarter-driver-*/jumpstarter_driver_*/client.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver client CLIs should either implement known classes that provide a CLI interface, or implement their own CLI interface. Use CompositeClient from jumpstarter_driver_composite.client for composite drivers with child drivers.

Files:

  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py
packages/jumpstarter-driver-**/jumpstarter_driver_**/*.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver implementations should follow existing code style validated with make lint (fix with make lint-fix), perform static type checking with make ty-pkg-${package_name}, add comprehensive tests, and verify all tests pass with make test-pkg-${package_name} or make test

Files:

  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Ruff should be used for code formatting and linting, excluding jumpstarter-protocol package

Files:

  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py
packages/jumpstarter-driver-*/jumpstarter_driver_*/driver.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver class names should be in CamelCase and be descriptive with appropriate suffixes based on functionality: Power drivers should end with *Power, Network drivers with *Network, Flasher drivers with *Flasher, Console drivers with *Console, Server drivers with *Server

Files:

  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py
packages/jumpstarter-driver-*/pyproject.toml

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver package names should be lowercase with hyphens for multi-word names (e.g., my-driver, custom-power, device-controller)

packages/jumpstarter-driver-*/pyproject.toml: Driver packages must follow the naming pattern jumpstarter-driver-<name>
Driver packages must register via the jumpstarter.drivers entry point in pyproject.toml
Driver packages must depend on jumpstarter and specific hardware libraries in their pyproject.toml

Files:

  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
packages/*/pyproject.toml

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Each package's pyproject.toml must include project metadata with Apache-2.0 license only

Files:

  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
🧠 Learnings (13)
📓 Common learnings
Learnt from: bennyz
Repo: jumpstarter-dev/jumpstarter PR: 241
File: packages/jumpstarter-driver-tftp/jumpstarter_driver_tftp/client.py:52-60
Timestamp: 2025-01-29T11:52:43.554Z
Learning: The TFTP driver (packages/jumpstarter-driver-tftp/jumpstarter_driver_tftp/driver.py) handles all low-level concerns like path validation, error handling, and checksum computation. The client (packages/jumpstarter-driver-tftp/jumpstarter_driver_tftp/client.py) should remain simple as it delegates these responsibilities to the driver.
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/jumpstarter_driver_*/ : Driver packages must implement a `client.py` file containing the client implementation
Learnt from: bennyz
Repo: jumpstarter-dev/jumpstarter PR: 267
File: packages/jumpstarter-driver-tftp/jumpstarter_driver_tftp/driver.py:32-40
Timestamp: 2025-02-08T16:08:34.616Z
Learning: In the TFTP driver (jumpstarter_driver_tftp), the TFTP server component is read-only, while file management operations (upload, delete) are handled through the driver's client interface, not through the TFTP protocol itself.
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/jumpstarter_driver_*/ : Driver packages must implement a `driver.py` file containing the driver implementation
Learnt from: michalskrivanek
Repo: jumpstarter-dev/jumpstarter PR: 608
File: packages/jumpstarter-cli/jumpstarter_cli/run.py:0-0
Timestamp: 2025-09-06T05:25:18.184Z
Learning: The Shell driver in jumpstarter can spawn multiple processes and leave them behind, creating zombie processes that need to be reaped even in non-PID 1 scenarios.
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-*/jumpstarter_driver_*/client.py : Driver client CLIs should either implement known classes that provide a CLI interface, or implement their own CLI interface. Use `CompositeClient` from `jumpstarter_driver_composite.client` for composite drivers with child drivers.
📚 Learning: 2025-11-27T09:58:41.875Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-*/jumpstarter_driver_*/client.py : Driver client CLIs should either implement known classes that provide a CLI interface, or implement their own CLI interface. Use `CompositeClient` from `jumpstarter_driver_composite.client` for composite drivers with child drivers.

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py
📚 Learning: 2025-11-27T09:58:55.346Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/jumpstarter_driver_*/ : Driver packages must implement a `client.py` file containing the client implementation

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py
  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
📚 Learning: 2025-11-27T09:58:41.875Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-**/jumpstarter_driver_**/*.py : Driver implementations should follow existing code style validated with `make lint` (fix with `make lint-fix`), perform static type checking with `make ty-pkg-${package_name}`, add comprehensive tests, and verify all tests pass with `make test-pkg-${package_name}` or `make test`

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py
  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
📚 Learning: 2025-11-27T09:58:55.346Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/pyproject.toml : Driver packages must register via the `jumpstarter.drivers` entry point in `pyproject.toml`

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
📚 Learning: 2025-11-27T09:58:55.346Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/pyproject.toml : Driver packages must depend on `jumpstarter` and specific hardware libraries in their `pyproject.toml`

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
📚 Learning: 2025-11-27T09:58:55.346Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-cli-*/pyproject.toml : CLI packages must depend on `jumpstarter` and `jumpstarter-cli-common` in their `pyproject.toml`

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
📚 Learning: 2025-11-27T09:58:55.346Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/pyproject.toml : Driver packages must follow the naming pattern `jumpstarter-driver-<name>`

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
📚 Learning: 2025-11-27T09:58:41.875Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-*/pyproject.toml : Driver package names should be lowercase with hyphens for multi-word names (e.g., `my-driver`, `custom-power`, `device-controller`)

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
📚 Learning: 2025-11-27T09:58:55.346Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to examples/*/pyproject.toml : Example packages should depend on relevant driver packages in their `pyproject.toml`

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
📚 Learning: 2025-11-27T09:58:55.346Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/jumpstarter_driver_*/ : Driver packages must implement a `driver.py` file containing the driver implementation

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
📚 Learning: 2025-11-27T09:58:55.346Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/*/pyproject.toml : Each package's `pyproject.toml` must include project metadata with Apache-2.0 license only

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
📚 Learning: 2025-11-27T09:58:41.875Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-composite/pyproject.toml : Composite drivers that have child drivers should inherit from `CompositeClient` in `jumpstarter_driver_composite.client` and have a dependency on `jumpstarter-driver-composite` in `pyproject.toml`

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
🧬 Code graph analysis (3)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py (2)
packages/jumpstarter-driver-network/jumpstarter_driver_network/adapters/portforward.py (1)
  • TcpPortforwardAdapter (20-32)
packages/jumpstarter/jumpstarter/client/base.py (2)
  • DriverClient (19-105)
  • call (42-52)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py (3)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py (8)
  • client (264-265)
  • SSHMITM (202-524)
  • SSHMITMError (43-44)
  • close (113-137)
  • close (520-524)
  • _load_private_key (282-302)
  • _handle_session (377-428)
  • connect (432-482)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py (4)
  • SSHMITMCommandRunResult (61-65)
  • execute (300-313)
  • run (99-121)
  • run (315-317)
packages/jumpstarter/jumpstarter/client/core.py (1)
  • DriverMethodNotImplemented (39-42)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py (5)
packages/jumpstarter/jumpstarter/driver/base.py (1)
  • Driver (56-273)
packages/jumpstarter/jumpstarter/streams/common.py (1)
  • create_memory_stream (40-45)
packages/jumpstarter-driver-network/jumpstarter_driver_network/driver.py (1)
  • TcpNetwork (88-121)
packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/driver.py (1)
  • SSHWrapper (9-50)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py (1)
  • forward (162-170)
🪛 GitHub Actions: Lint
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py

[error] 15-15: RUFF: Import block is un-sorted or un-formatted.

🪛 GitHub Check: ruff
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py

[failure] 173-173: Ruff (W293)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py:173:1: W293 Blank line contains whitespace


[failure] 171-171: Ruff (W293)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py:171:1: W293 Blank line contains whitespace


[failure] 165-165: Ruff (W293)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py:165:1: W293 Blank line contains whitespace


[failure] 144-144: Ruff (W293)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py:144:1: W293 Blank line contains whitespace


[failure] 134-134: Ruff (W293)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py:134:1: W293 Blank line contains whitespace


[failure] 122-122: Ruff (W293)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py:122:1: W293 Blank line contains whitespace


[failure] 80-80: Ruff (W293)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py:80:1: W293 Blank line contains whitespace


[failure] 77-77: Ruff (C901)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py:77:9: C901 cli is too complex (11 > 10)


[failure] 72-72: Ruff (W293)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py:72:1: W293 Blank line contains whitespace


[failure] 15-31: Ruff (I001)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py:15:1: I001 Import block is un-sorted or un-formatted

🪛 Gitleaks (8.30.0)
packages/jumpstarter-driver-ssh-mitm/examples/exporter.yaml

[high] 13-16: Identified a Private Key, which may compromise cryptographic security and sensitive data encryption.

(private-key)

packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py

[high] 17-23: Identified a Private Key, which may compromise cryptographic security and sensitive data encryption.

(private-key)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: e2e
🔇 Additional comments (12)
packages/jumpstarter-driver-ssh-mitm/examples/exporter.yaml (1)

1-24: Example configuration looks good.

This is a well-structured example showing both inline key and file-based key options. The placeholder private key is clearly marked with "(your private key here)" making it obvious this needs to be replaced. The static analysis warning about the private key is a false positive since this is intentionally example/placeholder content.

packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py (4)

16-24: Test key is appropriate for unit testing.

The Ed25519 test key is a standard practice for unit testing SSH functionality. The Gitleaks warning is a false positive since this key is:

  1. Only used in test fixtures
  2. Not connected to any real system
  3. Clearly marked as test content

27-99: Comprehensive driver configuration tests.

Good coverage of configuration validation including:

  • Default configuration
  • Missing TCP child error
  • Missing identity error
  • Both identities provided error
  • gRPC access for default username
  • Identity file loading

269-294: Security tests properly validate key protection.

The tests correctly verify that get_ssh_identity cannot be called via gRPC while remaining accessible internally. This is a critical security property for the MITM driver.


379-412: Stream connection test uses proper async patterns.

The test correctly uses @pytest.mark.anyio and verifies the session handler thread is started. The monkeypatching approach for paramiko.Transport is appropriate for testing stream behavior without actual SSH connections.

packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py (1)

254-267: Heredoc approach for command execution is sound but consider edge cases.

The heredoc pattern with a unique token prevents shell interpolation issues. However, if the user's command itself contains the exact token string (extremely unlikely with UUID), it could cause issues. The current implementation is acceptable given the UUID-based token generation.

packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py (6)

48-137: StreamSocket implementation is well-designed.

The bidirectional bridge using socketpair and dedicated forwarding threads is a solid approach. The cleanup logic properly handles shutdown with timeouts and logs warnings for unclean shutdowns.


140-198: MITMServerInterface correctly delegates authentication to Jumpstarter.

The design that accepts all SSH authentication methods is appropriate since real authentication happens through Jumpstarter's lease system. The optional username filtering adds an extra layer of access control.


314-315: AutoAddPolicy is appropriate for DUT connections.

Since the driver connects to DUTs which may have varying or unknown host keys, AutoAddPolicy is the right choice here. The security boundary is maintained by Jumpstarter's lease system and the private key staying on the exporter.


430-482: Stream bridging implementation is well-structured.

The connect() method correctly bridges Jumpstarter's async streams to Paramiko's synchronous socket interface. The exception handling during cleanup appropriately suppresses errors from already-closed connections. The documentation about gRPC ExecuteBatchError tracebacks is helpful.


484-518: Command execution implementation is robust.

Good practices observed:

  • Empty command check with informative error
  • shlex.join for safe command construction
  • Proper resource cleanup in finally block
  • Exception handling returns error tuple instead of raising

520-524: Clean resource cleanup in close().

The close() method properly cleans up the SSH wrapper before calling the parent's close method.

Comment thread packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py Outdated
Comment thread packages/jumpstarter-driver-ssh-mitm/pyproject.toml
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (7)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py (2)

255-268: Potential command injection if user input contains the heredoc delimiter.

While using a UUID-based heredoc token is a good approach, there's a theoretical (though extremely unlikely) edge case: if cmd somehow contains the exact token string on its own line, it could prematurely terminate the heredoc. Given the UUID randomness this is practically impossible, but for defense in depth, consider validating or escaping.

The current implementation is safe in practice due to the UUID's entropy. No change required unless you want extra paranoia.


296-299: Blocking sleep in forward loop could be improved with signal handling.

The time.sleep(1) loop works but is a polling pattern. This is acceptable for a simple forward command, but if responsiveness becomes an issue, consider using signal.pause() or an event-based approach.

packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py (4)

245-246: Consider using Ed25519 for the ephemeral host key.

RSA 2048 is secure but Ed25519 is faster for key generation and handshakes. Since this is an ephemeral MITM server key (regenerated each time), the performance difference could be noticeable.

-        # Generate ephemeral host key for MITM server
-        self._host_key = paramiko.RSAKey.generate(2048)
+        # Generate ephemeral host key for MITM server (Ed25519 for speed)
+        self._host_key = paramiko.Ed25519Key.generate()

343-348: Thread joins could block indefinitely if recv() hangs.

If the SSH connection enters a pathological state where recv() blocks forever without returning, these join() calls will block indefinitely. Consider adding a timeout or ensuring channels are closed to unblock the recv calls.

         t1.start()
         t2.start()
-        t1.join()
-        t2.join()
+        t1.join(timeout=30)
+        t2.join(timeout=30)
+        if t1.is_alive() or t2.is_alive():
+            self.logger.warning("Proxy threads did not exit cleanly")

397-410: Client channel may not be explicitly closed after shell sessions.

In exec mode (lines 399-406), client_channel.close() is called after forwarding exit status. In shell mode, the channel is only closed by _proxy_channels when dst.close() is called in the forward function. This should work, but explicitly closing client_channel in the finally block would be more robust.

         finally:
+            if client_channel:
+                try:
+                    client_channel.close()
+                except Exception:
+                    pass
             if dut_channel:
                 try:
                     dut_channel.close()

497-498: Decoding stdout/stderr with default UTF-8 may fail on binary output.

If a command outputs binary data, .decode() will raise UnicodeDecodeError. Consider using errors='replace' or errors='backslashreplace' for robustness.

-            stdout_data = stdout.read().decode()
-            stderr_data = stderr.read().decode()
+            stdout_data = stdout.read().decode(errors='replace')
+            stderr_data = stderr.read().decode(errors='replace')
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py (1)

267-291: Consider adding a test for double-close safety.

The cleanup tests verify that close() doesn't raise, but consider adding a test that calls close() twice to ensure idempotency.

def test_close_idempotent(self):
    """Test that close() can be called multiple times safely"""
    instance = SSHMITM(
        children={"tcp": TcpNetwork(host="127.0.0.1", port=22)},
        default_username="testuser",
        ssh_identity=TEST_SSH_KEY,
    )
    
    instance.close()
    instance.close()  # Should not raise
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5a8b654 and ed98974.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • packages/jumpstarter-driver-ssh-mitm/README.md (1 hunks)
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py (1 hunks)
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py (1 hunks)
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py (1 hunks)
  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/jumpstarter-driver-ssh-mitm/README.md
🧰 Additional context used
📓 Path-based instructions (6)
packages/jumpstarter-driver-*/jumpstarter_driver_*/client.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver client CLIs should either implement known classes that provide a CLI interface, or implement their own CLI interface. Use CompositeClient from jumpstarter_driver_composite.client for composite drivers with child drivers.

Files:

  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py
packages/jumpstarter-driver-**/jumpstarter_driver_**/*.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver implementations should follow existing code style validated with make lint (fix with make lint-fix), perform static type checking with make ty-pkg-${package_name}, add comprehensive tests, and verify all tests pass with make test-pkg-${package_name} or make test

Files:

  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Ruff should be used for code formatting and linting, excluding jumpstarter-protocol package

Files:

  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py
packages/jumpstarter-driver-*/pyproject.toml

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver package names should be lowercase with hyphens for multi-word names (e.g., my-driver, custom-power, device-controller)

packages/jumpstarter-driver-*/pyproject.toml: Driver packages must follow the naming pattern jumpstarter-driver-<name>
Driver packages must register via the jumpstarter.drivers entry point in pyproject.toml
Driver packages must depend on jumpstarter and specific hardware libraries in their pyproject.toml

Files:

  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
packages/*/pyproject.toml

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Each package's pyproject.toml must include project metadata with Apache-2.0 license only

Files:

  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
packages/jumpstarter-driver-*/jumpstarter_driver_*/driver.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver class names should be in CamelCase and be descriptive with appropriate suffixes based on functionality: Power drivers should end with *Power, Network drivers with *Network, Flasher drivers with *Flasher, Console drivers with *Console, Server drivers with *Server

Files:

  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py
🧠 Learnings (16)
📓 Common learnings
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/pyproject.toml : Driver packages must register via the `jumpstarter.drivers` entry point in `pyproject.toml`
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/jumpstarter_driver_*/ : Driver packages must implement a `driver.py` file containing the driver implementation
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/jumpstarter_driver_*/ : Driver packages must implement a `client.py` file containing the client implementation
Learnt from: bennyz
Repo: jumpstarter-dev/jumpstarter PR: 241
File: packages/jumpstarter-driver-tftp/jumpstarter_driver_tftp/client.py:52-60
Timestamp: 2025-01-29T11:52:43.554Z
Learning: The TFTP driver (packages/jumpstarter-driver-tftp/jumpstarter_driver_tftp/driver.py) handles all low-level concerns like path validation, error handling, and checksum computation. The client (packages/jumpstarter-driver-tftp/jumpstarter_driver_tftp/client.py) should remain simple as it delegates these responsibilities to the driver.
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/pyproject.toml : Driver packages must depend on `jumpstarter` and specific hardware libraries in their `pyproject.toml`
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-**/jumpstarter_driver_**/*.py : Driver implementations should follow existing code style validated with `make lint` (fix with `make lint-fix`), perform static type checking with `make ty-pkg-${package_name}`, add comprehensive tests, and verify all tests pass with `make test-pkg-${package_name}` or `make test`
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/pyproject.toml : Driver packages must follow the naming pattern `jumpstarter-driver-<name>`
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-cli-*/pyproject.toml : CLI packages must depend on `jumpstarter` and `jumpstarter-cli-common` in their `pyproject.toml`
📚 Learning: 2025-11-27T09:58:55.346Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/jumpstarter_driver_*/ : Driver packages must implement a `client.py` file containing the client implementation

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py
  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py
📚 Learning: 2025-11-27T09:58:41.875Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-*/jumpstarter_driver_*/client.py : Driver client CLIs should either implement known classes that provide a CLI interface, or implement their own CLI interface. Use `CompositeClient` from `jumpstarter_driver_composite.client` for composite drivers with child drivers.

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py
📚 Learning: 2025-11-27T09:58:55.346Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to **/*.py : Ruff should be used for code formatting and linting, excluding `jumpstarter-protocol` package

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py
📚 Learning: 2025-11-27T09:58:41.875Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-**/jumpstarter_driver_**/*.py : Driver implementations should follow existing code style validated with `make lint` (fix with `make lint-fix`), perform static type checking with `make ty-pkg-${package_name}`, add comprehensive tests, and verify all tests pass with `make test-pkg-${package_name}` or `make test`

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py
  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
📚 Learning: 2025-11-27T09:58:55.346Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/pyproject.toml : Driver packages must register via the `jumpstarter.drivers` entry point in `pyproject.toml`

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
📚 Learning: 2025-11-27T09:58:55.346Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/pyproject.toml : Driver packages must depend on `jumpstarter` and specific hardware libraries in their `pyproject.toml`

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
📚 Learning: 2025-11-27T09:58:55.346Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/pyproject.toml : Driver packages must follow the naming pattern `jumpstarter-driver-<name>`

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
📚 Learning: 2025-11-27T09:58:55.346Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-cli-*/pyproject.toml : CLI packages must depend on `jumpstarter` and `jumpstarter-cli-common` in their `pyproject.toml`

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
📚 Learning: 2025-11-27T09:58:41.875Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-*/pyproject.toml : Driver package names should be lowercase with hyphens for multi-word names (e.g., `my-driver`, `custom-power`, `device-controller`)

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
📚 Learning: 2025-11-27T09:58:55.346Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to examples/*/pyproject.toml : Example packages should depend on relevant driver packages in their `pyproject.toml`

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
📚 Learning: 2025-11-27T09:58:55.346Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/jumpstarter_driver_*/ : Driver packages must implement a `driver.py` file containing the driver implementation

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py
📚 Learning: 2025-11-27T09:58:55.346Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/*/pyproject.toml : Each package's `pyproject.toml` must include project metadata with Apache-2.0 license only

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
📚 Learning: 2025-11-27T09:58:41.875Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-composite/pyproject.toml : Composite drivers that have child drivers should inherit from `CompositeClient` in `jumpstarter_driver_composite.client` and have a dependency on `jumpstarter-driver-composite` in `pyproject.toml`

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
📚 Learning: 2025-11-27T09:58:41.875Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-*/jumpstarter_driver_*/driver.py : Driver class names should be in CamelCase and be descriptive with appropriate suffixes based on functionality: Power drivers should end with `*Power`, Network drivers with `*Network`, Flasher drivers with `*Flasher`, Console drivers with `*Console`, Server drivers with `*Server`

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
📚 Learning: 2025-01-29T11:52:43.554Z
Learnt from: bennyz
Repo: jumpstarter-dev/jumpstarter PR: 241
File: packages/jumpstarter-driver-tftp/jumpstarter_driver_tftp/client.py:52-60
Timestamp: 2025-01-29T11:52:43.554Z
Learning: The TFTP driver (packages/jumpstarter-driver-tftp/jumpstarter_driver_tftp/driver.py) handles all low-level concerns like path validation, error handling, and checksum computation. The client (packages/jumpstarter-driver-tftp/jumpstarter_driver_tftp/client.py) should remain simple as it delegates these responsibilities to the driver.

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py
🧬 Code graph analysis (1)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py (4)
packages/jumpstarter-driver-network/jumpstarter_driver_network/driver.py (1)
  • TcpNetwork (88-121)
packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/driver.py (1)
  • SSHWrapper (9-50)
packages/jumpstarter/jumpstarter/driver/base.py (1)
  • Driver (56-273)
packages/jumpstarter/jumpstarter/streams/common.py (1)
  • create_memory_stream (40-45)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: Redirect rules - jumpstarter-docs
  • GitHub Check: Header rules - jumpstarter-docs
  • GitHub Check: Pages changed - jumpstarter-docs
  • GitHub Check: e2e
  • GitHub Check: build
  • GitHub Check: pytest-matrix (macos-15, 3.13)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.13)
  • GitHub Check: pytest-matrix (macos-15, 3.12)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.12)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.11)
  • GitHub Check: pytest-matrix (macos-15, 3.11)
🔇 Additional comments (14)
packages/jumpstarter-driver-ssh-mitm/pyproject.toml (3)

1-10: Project metadata and build configuration look good.

The package correctly follows the naming pattern, includes Apache-2.0 licensing, specifies Python >=3.11, and uses standard hatch tooling with VCS versioning. The project metadata is complete with description, authors, readme, and homepage URLs.

Also applies to: 24-43


11-19: Verify all driver dependencies are necessary.

The package depends on several driver packages (jumpstarter-driver-composite, jumpstarter-driver-network, jumpstarter-driver-ssh). Confirm these are all actively used in the implementation by inspecting the imports in driver.py and client.py. If the SSH MITM driver can function independently for its core features, consider making unused dependencies optional or documenting their necessity.


21-22: No action needed on the entry point configuration.

Jumpstarter does not require custom entry-points for driver discovery. Drivers are loaded by module/class path through YAML configuration, not via entry point registration. The current entry point at lines 21-22 does not affect driver discoverability.

Likely an incorrect or invalid review comment.

packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py (3)

31-57: LGTM! Well-designed fallback command routing.

The DefaultCommandGroup implementation elegantly handles the case where users type commands directly (e.g., j ssh_mitm ls) by falling back to the run subcommand, while still providing helpful typo detection via get_close_matches.


190-194: Verify the security implications of disabled host key checking.

StrictHostKeyChecking=no and UserKnownHostsFile=/dev/null are intentional for this MITM proxy setup (the MITM server has an ephemeral host key), but ensure this is documented as expected behavior so users understand they're connecting to a proxy, not directly to the DUT.

The docstring and README should clarify that the client connects to the local MITM proxy (which has an ephemeral key), not directly to the DUT. This is a documentation concern, not a code issue.


301-318: LGTM! Clean delegation to driver via gRPC.

The execute and run methods correctly delegate to the driver's execute_command gRPC method and wrap results in the appropriate dataclass. The run alias is a sensible convenience.

packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py (4)

51-139: LGTM! Well-implemented bridge between async streams and paramiko.

The StreamSocket class correctly uses a socketpair with forwarding threads to bridge Jumpstarter's async streams to paramiko's blocking socket API. The cleanup logic in close() properly handles shutdown ordering and thread joining with timeouts.


141-194: LGTM! Auth delegation design is correct for MITM proxy.

The MITMServerInterface intentionally accepts all SSH authentication methods because the real authentication occurs through Jumpstarter's lease system. The optional username check adds a layer of validation without breaking the MITM flow.


463-469: Exception suppression during cleanup is intentional but worth noting.

The broad exception catch (line 463) suppresses all errors during cleanup, which is documented in the docstring (lines 436-439). This prevents framework errors when clients disconnect abruptly. The approach is pragmatic but means some errors won't be logged.


512-516: LGTM! Proper cleanup delegation.

The close() method correctly cleans up the internal _ssh_wrapper before calling super().close() to handle child drivers.

packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py (4)

16-24: Test SSH key is a valid Ed25519 key format.

Using a real key format (not just placeholder content) is good practice for testing key loading paths. This ensures the tests exercise actual parsing logic.


27-90: LGTM! Comprehensive configuration and access tests.

The TestSSHMITMDriver class covers all configuration validation paths (missing tcp, missing identity, both identities) and correctly tests that get_ssh_identity is not accessible via gRPC while get_default_username is.


107-244: LGTM! Thorough client execution tests.

The TestSSHMITMClient class covers important edge cases including command failures, multiple arguments, option-like arguments (e.g., ps -ef), and empty commands. The mock setup properly isolates the tests from actual SSH connections.


353-384: LGTM! Async stream test validates session handler startup.

The test_connect_starts_session test correctly verifies that the connect() stream spins up the session handler thread. The use of monkeypatch to replace paramiko.Transport with a dummy is appropriate for unit testing without actual SSH connections.

@mangelajo
Copy link
Copy Markdown
Member

@cursor can you review this?

@cursor
Copy link
Copy Markdown

cursor Bot commented Dec 4, 2025

Please finish setting up background agents. Go to Cursor

@mangelajo
Copy link
Copy Markdown
Member

tests are failing, look at the logs, seems like some dependencies could be missing from the pyproject on the driver.

@bkhizgiy
Copy link
Copy Markdown
Member Author

bkhizgiy commented Dec 4, 2025

@mangelajo yeah looking into it, will patch the fix soon, thanks :)

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py (2)

297-323: Consider adding explicit SSH connection timeouts to avoid long hangs

_create_dut_client calls paramiko.SSHClient.connect with all default timeouts. If the DUT host is firewalled or unresponsive, this can block for a long time, tying up whatever worker is running execute_command or the stream entrypoint.

It would be more robust to pass explicit timeouts (e.g., reusing channel_timeout or a dedicated configuration field) to connect, so failures fail fast instead of hanging:

-        client.connect(
-            hostname=target_host,
-            port=target_port,
-            username=username,
-            pkey=pkey,
-            look_for_keys=False,
-            allow_agent=False,
-        )
+        client.connect(
+            hostname=target_host,
+            port=target_port,
+            username=username,
+            pkey=pkey,
+            look_for_keys=False,
+            allow_agent=False,
+            timeout=self.channel_timeout,
+        )

Adjusting or splitting the timeout if needed (e.g., separate connect vs. channel timeouts) would keep the driver responsive under network issues.


476-505: Make stdout/stderr decoding more robust to non‑UTF‑8 output

execute_command decodes stdout and stderr with the default .decode(), which assumes UTF‑8 and will raise UnicodeDecodeError if the DUT emits arbitrary binary or legacy‑encoded output. That would drop you into the except block and return (1, "", str(e)), losing any real exit code and partial output.

You could avoid this by decoding with an explicit encoding and errors="replace" (or "ignore") so that command failures vs. encoding issues stay distinguishable:

-            exit_code = stdout.channel.recv_exit_status()
-            stdout_data = stdout.read().decode()
-            stderr_data = stderr.read().decode()
+            exit_code = stdout.channel.recv_exit_status()
+            stdout_data = stdout.read().decode(errors="replace")
+            stderr_data = stderr.read().decode(errors="replace")

This keeps exit_code, stdout, and stderr meaningful even when the output isn’t strict UTF‑8.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ed98974 and 0940121.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • packages/jumpstarter-driver-ssh-mitm/README.md (1 hunks)
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py (1 hunks)
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py (1 hunks)
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py (1 hunks)
  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/jumpstarter-driver-ssh-mitm/README.md
  • packages/jumpstarter-driver-ssh-mitm/pyproject.toml
🧰 Additional context used
📓 Path-based instructions (4)
packages/jumpstarter-driver-*/jumpstarter_driver_*/client.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver client CLIs should either implement known classes that provide a CLI interface, or implement their own CLI interface. Use CompositeClient from jumpstarter_driver_composite.client for composite drivers with child drivers.

Files:

  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py
packages/jumpstarter-driver-**/jumpstarter_driver_**/*.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver implementations should follow existing code style validated with make lint (fix with make lint-fix), perform static type checking with make ty-pkg-${package_name}, add comprehensive tests, and verify all tests pass with make test-pkg-${package_name} or make test

Files:

  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Ruff should be used for code formatting and linting, excluding jumpstarter-protocol package

Files:

  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py
packages/jumpstarter-driver-*/jumpstarter_driver_*/driver.py

📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)

Driver class names should be in CamelCase and be descriptive with appropriate suffixes based on functionality: Power drivers should end with *Power, Network drivers with *Network, Flasher drivers with *Flasher, Console drivers with *Console, Server drivers with *Server

Files:

  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/pyproject.toml : Driver packages must register via the `jumpstarter.drivers` entry point in `pyproject.toml`
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/jumpstarter_driver_*/ : Driver packages must implement a `driver.py` file containing the driver implementation
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/jumpstarter_driver_*/ : Driver packages must implement a `client.py` file containing the client implementation
Learnt from: bennyz
Repo: jumpstarter-dev/jumpstarter PR: 241
File: packages/jumpstarter-driver-tftp/jumpstarter_driver_tftp/client.py:52-60
Timestamp: 2025-01-29T11:52:43.554Z
Learning: The TFTP driver (packages/jumpstarter-driver-tftp/jumpstarter_driver_tftp/driver.py) handles all low-level concerns like path validation, error handling, and checksum computation. The client (packages/jumpstarter-driver-tftp/jumpstarter_driver_tftp/client.py) should remain simple as it delegates these responsibilities to the driver.
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/pyproject.toml : Driver packages must depend on `jumpstarter` and specific hardware libraries in their `pyproject.toml`
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-**/jumpstarter_driver_**/*.py : Driver implementations should follow existing code style validated with `make lint` (fix with `make lint-fix`), perform static type checking with `make ty-pkg-${package_name}`, add comprehensive tests, and verify all tests pass with `make test-pkg-${package_name}` or `make test`
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/pyproject.toml : Driver packages must follow the naming pattern `jumpstarter-driver-<name>`
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-cli-*/pyproject.toml : CLI packages must depend on `jumpstarter` and `jumpstarter-cli-common` in their `pyproject.toml`
📚 Learning: 2025-11-27T09:58:55.346Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-*/jumpstarter_driver_*/ : Driver packages must implement a `client.py` file containing the client implementation

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py
📚 Learning: 2025-11-27T09:58:41.875Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-*/jumpstarter_driver_*/client.py : Driver client CLIs should either implement known classes that provide a CLI interface, or implement their own CLI interface. Use `CompositeClient` from `jumpstarter_driver_composite.client` for composite drivers with child drivers.

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py
📚 Learning: 2025-11-27T09:58:55.346Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to **/*.py : Ruff should be used for code formatting and linting, excluding `jumpstarter-protocol` package

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py
📚 Learning: 2025-11-27T09:58:41.875Z
Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-**/jumpstarter_driver_**/*.py : Driver implementations should follow existing code style validated with `make lint` (fix with `make lint-fix`), perform static type checking with `make ty-pkg-${package_name}`, add comprehensive tests, and verify all tests pass with `make test-pkg-${package_name}` or `make test`

Applied to files:

  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py
  • packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py
🧬 Code graph analysis (2)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py (3)
packages/jumpstarter-driver-network/jumpstarter_driver_network/driver.py (1)
  • TcpNetwork (88-121)
packages/jumpstarter-driver-ssh/jumpstarter_driver_ssh/driver.py (1)
  • SSHWrapper (9-50)
packages/jumpstarter/jumpstarter/streams/common.py (1)
  • create_memory_stream (40-45)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py (2)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py (9)
  • client (257-258)
  • SSHMITM (197-516)
  • SSHMITMError (44-45)
  • get_ssh_identity (266-268)
  • close (116-138)
  • close (512-516)
  • _load_private_key (275-295)
  • _handle_session (371-422)
  • connect (426-474)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py (4)
  • SSHMITMCommandRunResult (61-66)
  • execute (301-314)
  • run (100-122)
  • run (316-318)
🪛 Gitleaks (8.30.0)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py

[high] 17-23: Identified a Private Key, which may compromise cryptographic security and sensitive data encryption.

(private-key)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: Redirect rules - jumpstarter-docs
  • GitHub Check: Header rules - jumpstarter-docs
  • GitHub Check: Pages changed - jumpstarter-docs
  • GitHub Check: e2e
  • GitHub Check: build
  • GitHub Check: pytest-matrix (macos-15, 3.12)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.11)
  • GitHub Check: pytest-matrix (macos-15, 3.13)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.12)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.13)
🔇 Additional comments (3)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py (1)

72-90: Good coverage of “key never leaves exporter” behavior

The test_identity_file and TestSSHMITMSecurity cases nicely assert that get_ssh_identity is not callable over gRPC (raising DriverMethodNotImplemented) while still being accessible via the driver instance itself. This is exactly the boundary needed to enforce that private keys stay on the exporter only.

No changes needed here.

Also applies to: 246-265

packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/client.py (1)

31-173: CLI wiring for run/shell/forward looks solid

DefaultCommandGroup plus ignore_unknown_options/allow_extra_args gives a nice UX: j ssh_mitm <cmd> falls back to the run subcommand, while ssh‑style flags are passed through untouched. The run, shell, and forward commands cleanly delegate into SSHMITMClient.execute, _launch_native_ssh, _run_shell, and _start_forward, matching the usage documented in the module docstring and the tests.

I don’t see functional issues here.

packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py (1)

232-269: Configuration validation and key handling match the intended security model

__post_init__ correctly enforces that a tcp child exists, that ssh_identity and ssh_identity_file are mutually exclusive, and that at least one identity source is provided. Delegating username and key resolution to an internal SSHWrapper instance, while only exporting get_default_username and keeping get_ssh_identity internal, aligns well with the “private key stays on the exporter” guarantee.

No issues here from a configuration or API-surface standpoint.

Comment thread packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py Outdated
Copy link
Copy Markdown
Member

@mangelajo mangelajo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I am requesting a medium/big change but I think it's necessary to avoid excessive duplication of code to maintain an debug in the long term.

Comment thread packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py Outdated
Comment thread packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py Outdated
Signed-off-by: Bella Khizgiyaev <bkhizgiy@redhat.com>
Signed-off-by: Bella Khizgiyaev <bkhizgiy@redhat.com>
Signed-off-by: Bella Khizgiyaev <bkhizgiy@redhat.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py (1)

13-20: Private key in test file triggers secret scanner.

The TEST_SSH_KEY constant contains an OpenSSH private key format that triggers Gitleaks. While this appears to be a test key, consider using a clearly non-secret dummy string since the tests mock Paramiko's key loading anyway.

🧹 Nitpick comments (2)
packages/jumpstarter-driver-ssh-mitm/README.md (1)

16-18: Add language specifier to fenced code block.

The architecture diagram code block should have a language specifier for proper rendering. Use text or plaintext for ASCII diagrams.

-```
+```text
 SSHWrapper --> SSHMITM --> TcpNetwork --> DUT

</blockquote></details>
<details>
<summary>packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py (1)</summary><blockquote>

`364-418`: **Consider extracting helper methods to reduce complexity.**

The `_handle_session` method slightly exceeds the cyclomatic complexity threshold (11 vs 10). While the current structure is logical and readable, extracting the exit status handling (lines 395-402) and the cleanup block (lines 407-418) into separate methods could improve maintainability.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used**: CodeRabbit UI

**Review profile**: CHILL

**Plan**: Pro

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 0940121e2ec8e7c321e7a489124fde287f3b7fe2 and a8d99c2d750ad6049f111e2aa39fc36adc3ad91b.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `uv.lock` is excluded by `!**/*.lock`

</details>

<details>
<summary>📒 Files selected for processing (5)</summary>

* `packages/jumpstarter-driver-ssh-mitm/README.md` (1 hunks)
* `packages/jumpstarter-driver-ssh-mitm/examples/exporter.yaml` (1 hunks)
* `packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py` (1 hunks)
* `packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py` (1 hunks)
* `packages/jumpstarter-driver-ssh-mitm/pyproject.toml` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* packages/jumpstarter-driver-ssh-mitm/pyproject.toml

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>📓 Path-based instructions (3)</summary>

<details>
<summary>packages/jumpstarter-driver-**/jumpstarter_driver_**/*.py</summary>


**📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)**

> Driver implementations should follow existing code style validated with `make lint` (fix with `make lint-fix`), perform static type checking with `make ty-pkg-${package_name}`, add comprehensive tests, and verify all tests pass with `make test-pkg-${package_name}` or `make test`

Files:
- `packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py`
- `packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py`

</details>
<details>
<summary>**/*.py</summary>


**📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)**

> Ruff should be used for code formatting and linting, excluding `jumpstarter-protocol` package

Files:
- `packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py`
- `packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py`

</details>
<details>
<summary>packages/jumpstarter-driver-*/jumpstarter_driver_*/driver.py</summary>


**📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)**

> Driver class names should be in CamelCase and be descriptive with appropriate suffixes based on functionality: Power drivers should end with `*Power`, Network drivers with `*Network`, Flasher drivers with `*Flasher`, Console drivers with `*Console`, Server drivers with `*Server`

Files:
- `packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py`

</details>

</details><details>
<summary>🧠 Learnings (7)</summary>

<details>
<summary>📓 Common learnings</summary>

Learnt from: bennyz
Repo: jumpstarter-dev/jumpstarter PR: 241
File: packages/jumpstarter-driver-tftp/jumpstarter_driver_tftp/client.py:52-60
Timestamp: 2025-01-29T11:52:43.554Z
Learning: The TFTP driver (packages/jumpstarter-driver-tftp/jumpstarter_driver_tftp/driver.py) handles all low-level concerns like path validation, error handling, and checksum computation. The client (packages/jumpstarter-driver-tftp/jumpstarter_driver_tftp/client.py) should remain simple as it delegates these responsibilities to the driver.


Learnt from: bennyz
Repo: jumpstarter-dev/jumpstarter PR: 267
File: packages/jumpstarter-driver-tftp/jumpstarter_driver_tftp/driver.py:32-40
Timestamp: 2025-02-08T16:08:34.616Z
Learning: In the TFTP driver (jumpstarter_driver_tftp), the TFTP server component is read-only, while file management operations (upload, delete) are handled through the driver's client interface, not through the TFTP protocol itself.


Learnt from: michalskrivanek
Repo: jumpstarter-dev/jumpstarter PR: 608
File: packages/jumpstarter-cli/jumpstarter_cli/run.py:0-0
Timestamp: 2025-09-06T05:25:18.184Z
Learning: The Shell driver in jumpstarter can spawn multiple processes and leave them behind, creating zombie processes that need to be reaped even in non-PID 1 scenarios.


Learnt from: michalskrivanek
Repo: jumpstarter-dev/jumpstarter PR: 589
File: packages/jumpstarter/jumpstarter/exporter/exporter.py:147-155
Timestamp: 2025-08-14T13:11:35.034Z
Learning: In the jumpstarter fork-based architecture, when the exporter's serve() method exits (e.g., due to lease changes), the child process terminates and the parent process automatically restarts it, eliminating concerns about orphaned tasks since the entire process is restarted.


</details>
<details>
<summary>📚 Learning: 2025-11-27T09:58:41.875Z</summary>

Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-/jumpstarter_driver_/*.py : Driver implementations should follow existing code style validated with make lint (fix with make lint-fix), perform static type checking with make ty-pkg-${package_name}, add comprehensive tests, and verify all tests pass with make test-pkg-${package_name} or make test


**Applied to files:**
- `packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py`

</details>
<details>
<summary>📚 Learning: 2025-11-27T09:58:55.346Z</summary>

Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-/jumpstarter_driver_/ : Driver packages must implement a driver.py file containing the driver implementation


**Applied to files:**
- `packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py`

</details>
<details>
<summary>📚 Learning: 2025-11-27T09:58:55.346Z</summary>

Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-27T09:58:55.346Z
Learning: Applies to packages/jumpstarter-driver-/jumpstarter_driver_/ : Driver packages must implement a client.py file containing the client implementation


**Applied to files:**
- `packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py`

</details>
<details>
<summary>📚 Learning: 2025-01-29T11:52:43.554Z</summary>

Learnt from: bennyz
Repo: jumpstarter-dev/jumpstarter PR: 241
File: packages/jumpstarter-driver-tftp/jumpstarter_driver_tftp/client.py:52-60
Timestamp: 2025-01-29T11:52:43.554Z
Learning: The TFTP driver (packages/jumpstarter-driver-tftp/jumpstarter_driver_tftp/driver.py) handles all low-level concerns like path validation, error handling, and checksum computation. The client (packages/jumpstarter-driver-tftp/jumpstarter_driver_tftp/client.py) should remain simple as it delegates these responsibilities to the driver.


**Applied to files:**
- `packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py`

</details>
<details>
<summary>📚 Learning: 2025-11-27T09:58:41.875Z</summary>

Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-/jumpstarter_driver_/driver.py : Driver class names should be in CamelCase and be descriptive with appropriate suffixes based on functionality: Power drivers should end with *Power, Network drivers with *Network, Flasher drivers with *Flasher, Console drivers with *Console, Server drivers with *Server


**Applied to files:**
- `packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py`

</details>
<details>
<summary>📚 Learning: 2025-11-27T09:58:41.875Z</summary>

Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-/jumpstarter_driver_/client.py : Driver client CLIs should either implement known classes that provide a CLI interface, or implement their own CLI interface. Use CompositeClient from jumpstarter_driver_composite.client for composite drivers with child drivers.


**Applied to files:**
- `packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py`

</details>

</details><details>
<summary>🧬 Code graph analysis (2)</summary>

<details>
<summary>packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py (1)</summary><blockquote>

<details>
<summary>packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py (9)</summary>

* `SSHMITM` (207-459)
* `SSHMITMError` (32-33)
* `client` (240-241)
* `_get_ssh_identity` (243-252)
* `close` (111-138)
* `_load_private_key` (259-277)
* `_handle_session` (364-418)
* `connect` (422-459)
* `MITMServerInterface` (141-203)

</details>

</blockquote></details>
<details>
<summary>packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py (2)</summary><blockquote>

<details>
<summary>packages/jumpstarter-driver-network/jumpstarter_driver_network/driver.py (1)</summary>

* `TcpNetwork` (88-121)

</details>
<details>
<summary>packages/jumpstarter/jumpstarter/streams/common.py (1)</summary>

* `create_memory_stream` (40-45)

</details>

</blockquote></details>

</details><details>
<summary>🪛 GitHub Actions: Lint</summary>

<details>
<summary>packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py</summary>

[error] 281-281: Ruff check failed: W293 Blank line contains whitespace. Remove trailing spaces on blank line. Command: 'ruff check'.

</details>

</details>
<details>
<summary>🪛 GitHub Check: ruff</summary>

<details>
<summary>packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py</summary>

[failure] 362-362: Ruff (W293)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py:362:1: W293 Blank line contains whitespace

---

[failure] 110-110: Ruff (F401)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py:110:45: F401 `jumpstarter.client.core.DriverMethodNotImplemented` imported but unused

---

[failure] 109-111: Ruff (I001)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py:109:1: I001 Import block is un-sorted or un-formatted

</details>
<details>
<summary>packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py</summary>

[failure] 364-364: Ruff (C901)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py:364:9: C901 `_handle_session` is too complex (11 > 10)

---

[failure] 281-281: Ruff (W293)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py:281:1: W293 Blank line contains whitespace

</details>

</details>
<details>
<summary>🪛 Gitleaks (8.30.0)</summary>

<details>
<summary>packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py</summary>

[high] 13-19: Identified a Private Key, which may compromise cryptographic security and sensitive data encryption.

(private-key)

</details>

</details>
<details>
<summary>🪛 markdownlint-cli2 (0.18.1)</summary>

<details>
<summary>packages/jumpstarter-driver-ssh-mitm/README.md</summary>

16-16: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)</summary>

* GitHub Check: Redirect rules - jumpstarter-docs
* GitHub Check: Header rules - jumpstarter-docs
* GitHub Check: Pages changed - jumpstarter-docs
* GitHub Check: pytest-matrix (macos-15, 3.12)
* GitHub Check: pytest-matrix (ubuntu-24.04, 3.12)
* GitHub Check: pytest-matrix (ubuntu-24.04, 3.11)
* GitHub Check: pytest-matrix (ubuntu-24.04, 3.13)
* GitHub Check: pytest-matrix (macos-15, 3.13)
* GitHub Check: e2e

</details>

<details>
<summary>🔇 Additional comments (8)</summary><blockquote>

<details>
<summary>packages/jumpstarter-driver-ssh-mitm/README.md (1)</summary><blockquote>

`1-111`: **Well-structured documentation.**

The README provides clear installation instructions, architecture overview, and configuration examples that align well with the driver implementation.

</blockquote></details>
<details>
<summary>packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py (1)</summary><blockquote>

`306-373`: **Good integration test for MITM proxy.**

The `test_mitm_proxy_forwards_data` test comprehensively verifies the proxy behavior including data forwarding, exit status propagation, and cleanup. The mocking strategy properly isolates the test from actual SSH connections.

</blockquote></details>
<details>
<summary>packages/jumpstarter-driver-ssh-mitm/examples/exporter.yaml (1)</summary><blockquote>

`1-33`: **Clear example configuration.**

The exporter configuration example effectively demonstrates the driver hierarchy and both key configuration options. The comments provide helpful guidance for users.

</blockquote></details>
<details>
<summary>packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py (5)</summary><blockquote>

`39-139`: **Well-implemented stream-to-socket bridge.**

The `StreamSocket` class properly handles bidirectional forwarding between async streams and blocking sockets. The cleanup logic with timeout handling and graceful thread shutdown is appropriate for this use case.

---

`141-204`: **Authentication bypass is intentional and documented.**

The `MITMServerInterface` correctly implements the MITM pattern where clients are pre-authenticated through Jumpstarter's lease system. The comment at lines 143-145 documents this design decision appropriately.

---

`206-241`: **LGTM - Driver configuration and validation.**

The `SSHMITM` class properly validates configuration constraints and generates an ephemeral host key to avoid fingerprint persistence issues. The decision to use `NetworkClient` as the client binding is correct for this network-layer driver.

---

`420-459`: **Proper async stream endpoint implementation.**

The `connect` method correctly implements the stream endpoint pattern with proper resource cleanup. The daemon thread with join timeout ensures the server thread doesn't leak.

---

`293-294`: **Note: AutoAddPolicy disables host key verification for DUT.**

`AutoAddPolicy` accepts any host key from the DUT without verification. This is typical for lab/test environments but worth noting. If stricter security is needed in the future, consider adding an optional `known_hosts` configuration.

</blockquote></details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py (2)

13-20: Private key still embedded in test file.

This was previously flagged and marked as addressed, but the BEGIN OPENSSH PRIVATE KEY block remains in the code and continues to trigger secret scanners.


361-363: Trailing whitespace on blank line.

Line 362 contains trailing whitespace, which was flagged in a previous review.

🧹 Nitpick comments (1)
packages/jumpstarter-driver-ssh-mitm/README.md (1)

16-18: Add language identifier to fenced code block.

The architecture diagram block should specify a language (e.g., text) to satisfy markdown linting rules.

Apply this diff:

-```
+```text
 SSHWrapper --> SSHMITM --> TcpNetwork --> DUT

As per coding guidelines, this aligns with markdown best practices.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used**: CodeRabbit UI

**Review profile**: CHILL

**Plan**: Pro

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between a8d99c2d750ad6049f111e2aa39fc36adc3ad91b and 08876ac6bb3da10fdb4d9bf468165bac7f2a70af.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `uv.lock` is excluded by `!**/*.lock`

</details>

<details>
<summary>📒 Files selected for processing (5)</summary>

* `packages/jumpstarter-driver-ssh-mitm/README.md` (1 hunks)
* `packages/jumpstarter-driver-ssh-mitm/examples/exporter.yaml` (1 hunks)
* `packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py` (1 hunks)
* `packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py` (1 hunks)
* `packages/jumpstarter-driver-ssh-mitm/pyproject.toml` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (2)</summary>

* packages/jumpstarter-driver-ssh-mitm/pyproject.toml
* packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>📓 Path-based instructions (2)</summary>

<details>
<summary>packages/jumpstarter-driver-**/jumpstarter_driver_**/*.py</summary>


**📄 CodeRabbit inference engine (.cursor/rules/creating-new-drivers.mdc)**

> Driver implementations should follow existing code style validated with `make lint` (fix with `make lint-fix`), perform static type checking with `make ty-pkg-${package_name}`, add comprehensive tests, and verify all tests pass with `make test-pkg-${package_name}` or `make test`

Files:
- `packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py`

</details>
<details>
<summary>**/*.py</summary>


**📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)**

> Ruff should be used for code formatting and linting, excluding `jumpstarter-protocol` package

Files:
- `packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py`

</details>

</details><details>
<summary>🧠 Learnings (1)</summary>

<details>
<summary>📚 Learning: 2025-11-27T09:58:41.875Z</summary>

Learnt from: CR
Repo: jumpstarter-dev/jumpstarter PR: 0
File: .cursor/rules/creating-new-drivers.mdc:0-0
Timestamp: 2025-11-27T09:58:41.875Z
Learning: Applies to packages/jumpstarter-driver-/jumpstarter_driver_/*.py : Driver implementations should follow existing code style validated with make lint (fix with make lint-fix), perform static type checking with make ty-pkg-${package_name}, add comprehensive tests, and verify all tests pass with make test-pkg-${package_name} or make test


**Applied to files:**
- `packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py`

</details>

</details><details>
<summary>🧬 Code graph analysis (1)</summary>

<details>
<summary>packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py (1)</summary><blockquote>

<details>
<summary>packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver.py (9)</summary>

* `SSHMITM` (207-459)
* `SSHMITMError` (32-33)
* `client` (240-241)
* `_get_ssh_identity` (243-252)
* `close` (111-138)
* `_load_private_key` (259-277)
* `_handle_session` (364-418)
* `connect` (422-459)
* `MITMServerInterface` (141-203)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Gitleaks (8.30.0)</summary>

<details>
<summary>packages/jumpstarter-driver-ssh-mitm/jumpstarter_driver_ssh_mitm/driver_test.py</summary>

[high] 13-19: Identified a Private Key, which may compromise cryptographic security and sensitive data encryption.

(private-key)

</details>
<details>
<summary>packages/jumpstarter-driver-ssh-mitm/examples/exporter.yaml</summary>

[high] 25-27: Identified a Private Key, which may compromise cryptographic security and sensitive data encryption.

(private-key)

</details>

</details>
<details>
<summary>🪛 markdownlint-cli2 (0.18.1)</summary>

<details>
<summary>packages/jumpstarter-driver-ssh-mitm/README.md</summary>

16-16: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)</summary>

* GitHub Check: Redirect rules - jumpstarter-docs
* GitHub Check: Header rules - jumpstarter-docs
* GitHub Check: Pages changed - jumpstarter-docs
* GitHub Check: pytest-matrix (ubuntu-24.04, 3.13)
* GitHub Check: pytest-matrix (ubuntu-24.04, 3.11)
* GitHub Check: pytest-matrix (ubuntu-24.04, 3.12)
* GitHub Check: e2e

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Copy link
Copy Markdown
Member

@mangelajo mangelajo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I gave it a try, very cool!! ;D

jumpstarter ⚡local ➤ j ssh_mitm
[12/07/2025 20:56:43] INFO INFO:SSHMITM:MITM proxy established: client <-> DUT (mode=shell) driver.py:389
Last login: Sun Dec 7 20:56:04 2025 from ::1
➜ ~
➜ ~ exit
jumpstarter ⚡local ➤

@mangelajo mangelajo merged commit fa0a3b5 into jumpstarter-dev:main Dec 7, 2025
18 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Dec 12, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants