apple / apple/container

[Request]: Document shell evaluation in container machine run and consider an argv-preserving mode

Open
#1,954 0 comments 1 reaction 0 assignees View on GitHub
Dominant language
Swift
Stars
49.9k
Forks
1.8k
Avg merge
1d 20h
Merged PRs (30d)
22

Description

### Feature or enhancement request details

### Summary

`container machine run [] [ ...]` looks like an argv-preserving interface, but the guest concatenates the supplied command and arguments and evaluates them through the user's shell:

```sh
exec "${USER_SHELL:-${SHELL}}" -c "$*"
```

This behavior appears intentional: it is described in
https://github.com/apple/container/blob/4dfa3d49d910f1a024359de7e4aa4fa9be649e20/Sources/Plugins/MachineAPIServer/Resources/init#L11-L15 and covered by integration tests including `testRunWithEnvironment`, `testRunCommandInShell`, and `testRunCommandExitCode`:

https://github.com/apple/container/blob/4dfa3d49d910f1a024359de7e4aa4fa9be649e20/Tests/IntegrationTests/Machine/TestCLIMachineRuntimeSerial.swift#L179

https://github.com/apple/container/blob/4dfa3d49d910f1a024359de7e4aa4fa9be649e20/Tests/IntegrationTests/Machine/TestCLIMachineRuntimeSerial.swift#L240

https://github.com/apple/container/blob/4dfa3d49d910f1a024359de7e4aa4fa9be649e20/Tests/IntegrationTests/Machine/TestCLIMachineRuntimeSerial.swift#L254

The issue is that `container machine run --help` only describes the operands as:

```text
Command to run (default: login shell)
Command arguments
```

It does not mention that they are joined and evaluated as shell code. This can reasonably be mistaken for argv-preserving behavior, especially by wrapper or automation authors familiar with interfaces such as `docker exec`.

### Reproduction and comparison with `docker exec`

```console
# A single argument containing spaces
$ container machine run -n my-machine -- printf ':%s:' 'one two three'
:one::two::three:

$ docker exec my-container printf ':%s:' 'one two three'
:one two three:

# Command substitution inside an argument
$ container machine run -n my-machine -- printf ':%s:' 'x=$(id -u)'
:x=501:

$ docker exec my-container printf ':%s:' 'x=$(id -u)'
:x=$(id -u):
```

`docker exec` preserves the argument boundaries and does not evaluate `$(id -u)`. Shell evaluation occurs only when a shell is explicitly requested, for example:

```console
$ docker exec my-container sh -c 'printf ":%s:" "x=$(id -u)"'
:x=0:
```

This matches the documented `docker exec` contract: `COMMAND` must be an executable, and shell processing requires an explicit `sh -c`: https://docs.docker.com/reference/cli/docker/container/exec/

Runtime reproduction was reconfirmed on container 1.1.0 (build 5973b9c) on 2026-07-16. The same implementation is present on current `main` (4dfa3d4, source-checked 2026-07-16). The Docker comparison above was verified locally with Docker Engine 27.4.0 and `alpine:latest`.

### Why this matters

For a user who controls the complete command line, this grants no new capability. The concern is downstream code that forwards untrusted data as an argument while assuming argv semantics; a value such as `$(...)` is then evaluated as code in the guest.

### Requested changes

1. Document that the command and arguments are joined into a single command string and evaluated by the shell configured for the execution user inside the guest, including expansion, substitution, and redirection.
2. Consider adding an explicit opt-in argv-preserving mode, such as `--exec` or `--no-shell`, while retaining the current shell behavior for compatibility.

### Code of Conduct

- [x] I agree to follow this project's Code of Conduct

Contributor guide

Open the contributing guide

Research direction

Read the shell invocation in Sources/Plugins/MachineAPIServer/Resources/init and the integration coverage in Tests/IntegrationTests/Machine/TestCLIMachineRuntimeSerial.swift, especially testRunWithEnvironment, testRunCommandInShell, and testRunCommandExitCode. Document that arguments are joined and evaluated by the guest user's shell, then assess the requested argv-preserving mode and define its behavior and tests.

Written by the indexing model from the issue text.

Assessment

Tech stack
shell, swift
Domain
cli, documentation
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.