Skip to content

fix: close temp file before git reads it and handle Windows pipe/scanner errors - #355

Open
awdemos wants to merge 5 commits into
dagger:mainfrom
awdemos:fix/tiny-bug-sweep
Open

fix: close temp file before git reads it and handle Windows pipe/scanner errors#355
awdemos wants to merge 5 commits into
dagger:mainfrom
awdemos:fix/tiny-bug-sweep

Conversation

@awdemos

@awdemos awdemos commented Jul 25, 2026

Copy link
Copy Markdown

What this PR fixes

A focused bug-fix sweep of the repository, with an emphasis on security-validated inputs and safe resource handling. All changes are small, backwards-compatible fixes with regression tests where appropriate.

1. repository/git.go: temporary state file not closed before git notes reads it

Problem: saveState() created a temp file, deferred f.Close(), wrote the serialized state into it, and then passed the file path to git notes --ref container-use-state add -F <file> inside a lock. Because the file was still open when git was invoked, the close could race the git read. On Windows this is especially problematic because an open file handle prevents another process from reading the file. It also meant any write/close error was masked until function exit.

Fix: Close the temp file explicitly (and check the error) immediately after writing and before invoking git. The deferred close remains as a safety net for error paths.

2. cmd/container-use/watch_windows.go: ignored errors in runGitLogWindows()

Problem: The Windows-specific watch command piped git log output into a buffer and then wrote it to stdout, but it ignored scanner.Err(), ignored the return value of os.Stdout.Write(), and manually closed pipes when cmd.Start() failed without handling close errors.

Fix: Check scanner.Err() and os.Stdout.Write() errors, and return them to the caller. Handle pipe close errors cleanly; best-effort cleanup on cmd.Start() failure is annotated with //nolint:errcheck.

3. repository/git.go / repository/repository.go: git argument injection and path traversal from agent-controlled inputs

Problem:

  • Environment IDs, branch names, and git refs from CLI/MCP arguments were passed directly to RunGitCommand. A value starting with - could be interpreted by git as an option; whitespace or control characters could split into unexpected arguments.
  • exportEnvironmentFile() joined an agent-controlled target_file directly to the host worktree path with filepath.Join, allowing ../ traversal to write outside the environment worktree.

Fix:

  • Added validateGitRefComponent() and applied it in Checkout, Merge, Apply, and initializeWorktree.
  • Rejected absolute paths and ..-prefixed paths in exportEnvironmentFile() before any host filesystem or Dagger operation.
  • Added regression tests in repository/git_test.go.

4. environment/environment.go: arbitrary executable via user-controlled shell parameter

Problem: Environment.Run() built exec args as []string{shell, "-c", command} without validating shell. A caller could pass any executable path (e.g. /bin/echo) and have it invoked as the shell interpreter.

Fix: Added isAllowedShell() allowlist (sh, bash, zsh, ash and their /bin/* forms) and reject unsupported values before building args. Added regression test in environment/environment_test.go.

5. cmd/container-use/logger.go: debug log file world-readable

Problem: The default debug log file was opened with mode 0644, making it readable by any local user. The log can contain command output and environment metadata.

Fix: Open the log file with mode 0600.

Residual risk / intentionally not addressed in this PR

A parallel security audit also flagged the items below. They are either core product behavior or require larger design changes, so they are left for separate issues rather than being squeezed into a "tiny fix" PR:

  • Arbitrary command execution inside containers: Agents are intentionally allowed to run arbitrary setup_commands, install_commands, and environment_run_cmd commands inside their isolated containers. This is the product's purpose; the fix is policy/allowlist design, not a code patch.
  • Plaintext secrets in .container-use/environment.json: Moving to a secrets manager or encrypted store is an architecture change.
  • Submodule credential exposure: git submodule update --init --recursive runs with host credentials. Changing this requires design decisions about submodule trust boundaries.
  • Single-tenant MCP isolation: --single-tenant is documented as one environment per process; multi-session isolation would require protocol-level auth design.
  • PATH poisoning of dagger binary in terminal command: exec.LookPath("dagger") trusts PATH. A robust fix needs an agreed way to locate the Dagger binary (well-known paths, config, or absolute path). Left as future hardening.

Tests

  • go test -short -count=1 ./... passes for all packages:
    ok  	github.com/dagger/container-use/cmd/container-use
    ok  	github.com/dagger/container-use/cmd/container-use/agent
    ok  	github.com/dagger/container-use/environment
    ok  	github.com/dagger/container-use/environment/integration
    ok  	github.com/dagger/container-use/mcpserver
    ok  	github.com/dagger/container-use/repository
    
  • go vet ./... is clean.
  • go fmt ./... is clean.

Note: dagger call lint could not be run locally because the Dagger/Podman engine on this host is currently in a bad state (cannot re-exec process to join the existing user namespace), unrelated to these code changes.

Checklist

  • Bug fix (non-breaking change)
  • go test -short ./... passes
  • go vet ./... passes
  • go fmt ./... passes
  • Security regression tests added

Explicitly close and check the temporary state file after writing but before

passing its path to git notes. The previous deferred close happened after the

git command ran, which can fail on Windows (open handle prevents git from

reading the file) and masks write/close errors.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Check errors from scanner.Err() and os.Stdout.Write(), and close pipe ends

cleanly after reading. Previously these errors were silently discarded, which

could truncate output or leave pipe handles open on Windows without reporting

failure to the caller.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Add validateGitRefComponent to reject ref/branch names that start with '-' or

contain unsafe characters before passing them to git commands. Apply validation in

Checkout, Merge, Apply, and initializeWorktree to prevent argument injection.

Also reject absolute paths and '..' traversal in exportEnvironmentFile so that

agent-provided target_file cannot escape the environment worktree on the host

filesystem.

Regression tests included for ref validation and path traversal classification.
Reject unsupported shell strings before building container exec args. This prevents

a caller from passing an arbitrary executable path as the shell interpreter

(e.g. '/bin/echo') in environment_run_cmd.

Regression test included.
Change the default debug stderr log file mode from 0644 to 0600 so that other

users on the same host cannot read potentially sensitive command output or

environment metadata.
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