Skip to content

feat: raise the WinRM post-sync command limit to 8192 characters - #358

Open
bernie-g wants to merge 3 commits into
mainfrom
bernie/pki-348-increase-windows-post-command-character-limit-to-8192
Open

feat: raise the WinRM post-sync command limit to 8192 characters#358
bernie-g wants to merge 3 commits into
mainfrom
bernie/pki-348-increase-windows-post-command-character-limit-to-8192

Conversation

@bernie-g

@bernie-g bernie-g commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Description 📣

Sends the post-sync PowerShell script to the Windows host over stdin instead of on the command line, lifting the effective ~2555-character ceiling imposed by cmd.exe to the new 8192 product limit and removing the reason a command had to be ASCII.

Paired with Infisical/infisical#7620, which raises the matching schema cap. PKI-348.

Type ✨

  • Bug fix
  • New feature
  • Improvement
  • Breaking change
  • Documentation

Tests 🛠️

go build ./...
go vet ./packages/gateway-v2/winrm/
go test -race ./packages/gateway-v2/...

Unit coverage added for the UTF-16LE encoding (including surrogate pairs), the base64 payload round trip, and the transport serialization. The serialization test is paired with a control test asserting the same probe does observe overlap when unwrapped, so it cannot pass vacuously.

Live-host validation is still outstanding. The mechanism was developed against a Windows Server 2016 / PowerShell 5.1 host, but the NTLM serialization fix has not yet been exercised end to end by a real sync.


Send the script to the host over stdin rather than on the PowerShell
command line. cmd.exe rejects a command line past ~8155 characters
(measured against a live Server 2016 host) and -EncodedCommand inflates a
script ~3.2x on the way there, so the usable ceiling was ~2555.

A fixed bootstrap now takes the command line and reads the real script
from stdin. The script crosses base64-encoded, so the host's console code
page cannot alter it: the ASCII-only restriction is gone, and the pkcs12
password no longer appears in the host's process table.

Serialize SOAP requests per client. NTLM message sealing is RC4 keyed by a
sequence counter, and the library issues the stdin Send concurrently with
the output Receive without locking, desynchronizing the keystream and
failing with "checksum does not match". Commands that write stdin also use
a shorter WSMan operation timeout so the output poll releases the lock
promptly instead of long-polling for 60s.

Also shortens noOutcomeMessage to fit the control plane's 120-character
failure-detail cap, which previously cut it mid-quote and dropped the part
telling the operator what to do instead.
@linear

linear Bot commented Aug 11, 2026

Copy link
Copy Markdown

PKI-348

@infisical-review-police

Copy link
Copy Markdown

💬 Discussion in Slack: #pr-review-cli-358-feat-raise-the-winrm-post-sync-command-limit-to-8192-character

Posted by Review Police — reviews, comments, new commits, and CI failures will stream into this channel.

@greptile-apps

greptile-apps Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR moves WinRM post-sync PowerShell scripts from the command line to base64-encoded UTF-16LE stdin, allowing longer Unicode commands while serializing SOAP requests for NTLM session safety.

  • Raises the gateway command cap from 2048 to 8192.
  • Adds a fixed PowerShell bootstrap that reads and executes the stdin payload.
  • Serializes WinRM transport requests and shortens WSMan operation polling.
  • Adds encoding, payload round-trip, command-limit, and transport-concurrency tests.

Confidence Score: 3/5

The PR should not merge until the client-wide two-second WSMan timeout and Unicode byte-counting mismatch are corrected.

Slow WinRM setup or stdin operations can fail before the configured command deadline, and valid Unicode commands can be rejected below the advertised 8192-character limit.

Files Needing Attention: packages/gateway-v2/winrm/winrm.go, packages/gateway-v2/winrm_handler.go

Important Files Changed

Filename Overview
packages/gateway-v2/winrm/winrm.go Implements stdin-based script delivery and serialized SOAP transport, but applies the polling timeout to setup and input operations as well.
packages/gateway-v2/winrm_handler.go Raises the product limit to 8192 characters while retaining byte-based validation that rejects valid non-ASCII commands.
packages/gateway-v2/winrm/winrm_command_test.go Adds useful encoding and limit coverage, but command-limit tests exercise only single-byte ASCII input.
packages/gateway-v2/winrm/winrm_transport_test.go Verifies transport serialization and timeout configuration but not the effect of that timeout on non-polling operations.

Reviews (1): Last reviewed commit: "feat: raise the WinRM post-sync command ..." | Re-trigger Greptile

Comment thread packages/gateway-v2/winrm/winrm.go Outdated
Comment thread packages/gateway-v2/winrm_handler.go Outdated
The gateway measured the command with len(), a byte count, while the
control plane's limit counts characters. Now that a command may be
non-ASCII, 8192 accented characters is 16384 bytes, so the gateway
rejected a command the UI had already accepted, after the certificates
were delivered.

Also raises the gateway cap and reframes it as a backstop rather than a
second copy of the product limit. The command arrives with placeholders
substituted, and {{certificateFiles}} grows with the number of
certificates delivered, so it needs headroom over what an operator can
save.
The short timeout applied to every operation on the client, not just the
output poll it was meant for. Shell creation, Command, Send, Signal and
Delete inherited a two-second budget, and none of them retries on a
w:TimedOut fault, so a host that was merely slow failed the sync outright.

It existed only to break a standoff: the output poll held the transport
lock waiting for output, and no output could arrive until stdin was
written, which needed the same lock. The bootstrap now writes one byte
before it blocks on stdin, so the poll returns as soon as PowerShell
starts and releases the lock on its own. The timeout override is gone and
newClient is untouched again.

The sentinel is stripped with TrimPrefix, which removes a single
occurrence, so a command that itself opens by printing that byte keeps it.
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.

1 participant