Skip to content

SDK: LXC exec parses guest-controlled stdout as an error envelope before the trusted stderr channel #857

Description

Split out of review feedback on #849 so that PR stays scoped to the LXC state-aware lifecycle slice.

Problem

For a normal (streaming) LXC exec, stdout carries guest-controlled output, but the SDK parses stdout as protocol data before consulting the executor-owned stderr channel.

execInSandboxAsync (sdk/node/src/state-aware.ts:191-193):

const errorEnvelope =
  tryParseErrorEnvelope(stdout) ??
  (backendKey === 'lxc' ? tryParseErrorEnvelopeFromLines(stderr) : null);

tryParseErrorEnvelope (sdk/node/src/state-aware-helper.ts:175-188) whole-string parses: JSON.parse(stdout.trim()), returning the envelope whenever the result has an error.code.

So a guest script whose complete stdout is a valid {"error":{"code":...}} document and which exits nonzero is thrown to the caller as an MxcError, even though it ran normally and that JSON is simply its output. The guest can forge any error code the SDK surfaces.

The last-non-empty-line narrowing that the surrounding comment cites as the safeguard lives in tryParseErrorEnvelopeFromLines, which is only applied to stderr. Nothing narrows the stdout path.

Why it is not fixed in #849

The stdout parse is not dead code -- a non-streaming (dry-run) exec legitimately uses stdout as its single client-facing channel, so it cannot simply be deleted. Fixing this means deciding per dispatch mode which channel is authoritative and making stderr the sole trusted channel for streaming LXC execs, including parse-time errors. That is a change to the exec dispatch contract shared with the other backends, not a local edit.

Suggested fix

Give LXC exec one trusted channel (stderr) for dispatch and parse-time errors, and stop interpreting guest stdout as protocol data on that path. Keep the stdout parse only where the executor owns stdout.

Found by Copilot review on #849 (comment 3780192923).

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions