Skip to content

Build public remote tunnel toolkit#1

Merged
sumitake merged 1 commit into
mainfrom
codex/public-tunnel-toolkit
Jul 12, 2026
Merged

Build public remote tunnel toolkit#1
sumitake merged 1 commit into
mainfrom
codex/public-tunnel-toolkit

Conversation

@sumitake

Copy link
Copy Markdown
Owner

Summary

  • replace the placeholder repository with a generalized Node.js dev-tunnels relay and bounded Python MessagePack RPC client
  • enforce friendly-name or explicit-ID selection, connect-only scope, loopback-only listeners, an immutable port allowlist, token scrubbing, direct argv execution, protocol-v5 state validation, deadlines, output caps, and safe terminal rendering
  • add CLI-only VS Code host bootstrap, systemd/launchd lifecycle examples, architecture/deployment/recovery/decommission workflows, and public contribution/security documentation
  • isolate generalized VS Code interface fixtures under assets/vscode-interface/ with pinned-source attribution and an exact CC-BY-4.0 license copy; no extension code, binaries, or product assets are redistributed
  • add full-SHA-pinned CI, dual-language CodeQL, full-history Gitleaks, Dependabot, dependency audits, actionlint, Markdown lint, repository sanitation, and CODEOWNERS

Verification

  • 20 Node tests and executable SDK smoke check
  • 15 Python tests from a clean non-editable wheel install
  • Ruff lint and format checks
  • markdownlint-cli2 and actionlint
  • npm audit: zero known vulnerabilities
  • pip-audit of the locked Python runtime: zero known vulnerabilities
  • Gitleaks worktree and full-history scans: no leaks
  • repository validator and git diff --check
  • CC-BY-4.0 license matches the pinned upstream file exactly
  • live read-only relay/RPC test against an active existing tunnel: connected, opened only IPv4/IPv6 loopback listeners for two explicitly allowed ports, ran direct identity/hostname commands, escaped an injected terminal-control sequence, stopped on one signal, and closed every listener
  • independent Gemini 3.1 Pro high-effort architecture review converged before implementation
  • independent Gemini 3.1 Pro high-effort code/security review returned no actionable findings

Security impact

This creates a new authenticated tunnel client and private-protocol parser. The security invariants are fail-closed tunnel selection, connect-only service scope, no token in argv/config/logs, environment-token scrubbing, filter-before-connect on every fresh SDK client, no wildcard listener, bounded MessagePack and output state, no implicit shell, opt-in environment forwarding, safe output rendering by default, and full cleanup with an SDK cancellation token plus a post-disposal exit fallback for an observed upstream idle socket handle.

Compatibility and caveats

  • the remote host must already run an authenticated VS Code CLI tunnel; this project does not create one or replace OpenSSH
  • the private RPC interface is supported only at observed protocol version 5 and fails closed on drift
  • the SDK mirrors configured IPv4 loopback listeners on IPv6 loopback when available and emits its own local forwarding notices
  • CLI, Server, Remote extensions, and Microsoft cloud service terms remain separate from this repository

@github-advanced-security

Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

@sumitake sumitake merged commit 88170be into main Jul 12, 2026
13 checks passed
@sumitake sumitake deleted the codex/public-tunnel-toolkit branch July 12, 2026 00:06

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces the Remote-SSH-Tunnel project, featuring a Node.js loopback-only relay and a bounded Python MessagePack RPC client to interoperate with VS Code Remote Tunnels. The review feedback highlights several critical improvement opportunities: addressing a potential event listener leak on the abort signal in the Node.js client, replacing fragile strict key-matching checks in the Python RPC protocol parser with robust presence checks to prevent future compatibility failures, and reconfiguring standard output streams in the CLI to prevent crashes on non-UTF-8 terminal encodings.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/relay/sdk-client.js
Comment on lines +181 to +190
const outcome = await new Promise((resolve) => {
const abort = () => resolve();
signal.addEventListener("abort", abort, { once: true });
finalDisconnect.disconnected.then(resolve).finally(() => {
signal.removeEventListener("abort", abort);
});
});
if (outcome instanceof Error && !signal.aborted) {
throw outcome;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

There is a potential event listener leak on the signal object. If the signal is aborted before finalDisconnect.disconnected resolves, the promise returned by waitForDisconnect resolves and the function returns, but the 'abort' event listener is never removed because the .finally block is attached to finalDisconnect.disconnected.then(...), which remains pending. To prevent memory leaks in long-running supervisor loops, ensure the event listener is cleaned up immediately after the promise resolves.

      let cleanup;
      const outcome = await new Promise((resolve) => {
        const abort = () => resolve();
        signal.addEventListener("abort", abort, { once: true });
        cleanup = () => signal.removeEventListener("abort", abort);
        finalDisconnect.disconnected.then(resolve);
      });
      cleanup?.();
      if (outcome instanceof Error && !signal.aborted) {
        throw outcome;
      }

Comment on lines +227 to +228
if set(params) != {"version", "protocol_version"}:
raise ProtocolError("RPC hello has unexpected version fields")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Strict key matching (set(params) != {"version", "protocol_version"}) is fragile and violates the Robustness Principle. If Microsoft updates the VS Code Remote RPC protocol to include any new or extra fields in the version handshake payload, the client will fail with a ProtocolError. It is safer to only verify that the required fields are present.

Suggested change
if set(params) != {"version", "protocol_version"}:
raise ProtocolError("RPC hello has unexpected version fields")
if "version" not in params or "protocol_version" not in params:
raise ProtocolError("RPC hello is missing required version fields")

Comment on lines +303 to +305

expected_fields = {"stream"} if method == "stream_ended" else {"stream", "segment"}
if set(params) != expected_fields:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Strict key matching on stream notification parameters (set(params) != expected_fields) can cause the client to crash if any extra or new fields are added to the protocol in the future. It is more robust to only check for the presence of the required fields.

Suggested change
expected_fields = {"stream"} if method == "stream_ended" else {"stream", "segment"}
if set(params) != expected_fields:
if "stream" not in params or (method == "stream_data" and "segment" not in params):
raise ProtocolError("stream notification is missing required fields")

Comment on lines +332 to +333
if not isinstance(result, dict) or set(result) != {"exit_code", "message"}:
raise ProtocolError("RPC response has an invalid result shape")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Strict key matching on the command result map (set(result) != {"exit_code", "message"}) will cause a crash if the remote host adds any new fields (such as resource usage, signal info, etc.) to the result dictionary. Only verify that the required fields are present.

Suggested change
if not isinstance(result, dict) or set(result) != {"exit_code", "message"}:
raise ProtocolError("RPC response has an invalid result shape")
if not isinstance(result, dict) or "exit_code" not in result or "message" not in result:
raise ProtocolError("RPC response has an invalid result shape")

Comment on lines +113 to +114
stdout = OutputTarget(sys.stdout, sys.stdout.buffer, raw=args.raw_output)
stderr = OutputTarget(sys.stderr, sys.stderr.buffer, raw=args.raw_output)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

If the local terminal or console uses a non-UTF-8 encoding (such as CP1252 or CP437 on Windows) and the remote command outputs characters that cannot be represented in that encoding, writing to sys.stdout or sys.stderr will raise a UnicodeEncodeError and crash the CLI. Reconfigure sys.stdout and sys.stderr to use backslashreplace or replace error handlers to ensure the CLI remains robust across different terminal encodings.

    if hasattr(sys.stdout, "reconfigure"):
        sys.stdout.reconfigure(errors="backslashreplace")
    if hasattr(sys.stderr, "reconfigure"):
        sys.stderr.reconfigure(errors="backslashreplace")
    stdout = OutputTarget(sys.stdout, sys.stdout.buffer, raw=args.raw_output)
    stderr = OutputTarget(sys.stderr, sys.stderr.buffer, raw=args.raw_output)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 51b07917c2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

text = self._decoder.decode(data, final=final)
rendered: list[str] = []
for character in text:
if character in "\t\r\n":

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Escape carriage returns in safe output mode

When an untrusted remote command emits \r, safe mode writes it through unchanged, so a terminal returns the cursor to the start of the current line and subsequent bytes can overwrite or hide earlier output (for example, FAILED\rOK \n). That leaves a terminal-control path in the default renderer despite the surrounding code escaping other Cc controls; escape \r as well or normalize it instead of passing it through.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants