Skip to content

fix: eliminate nested bash layer in chroot command execution for Java/.NET#715

Merged
Mossaka merged 1 commit intomainfrom
copilot/fix-java-dotnet-chroot
Feb 12, 2026
Merged

fix: eliminate nested bash layer in chroot command execution for Java/.NET#715
Mossaka merged 1 commit intomainfrom
copilot/fix-java-dotnet-chroot

Conversation

Copy link
Contributor

Copilot AI commented Feb 12, 2026

Java and .NET runtimes fail inside AWF chroot mode because /proc/self/exe resolves to /bin/bash instead of the actual runtime binary. .NET CLR rejects execution with "cannot execute dotnet when renamed to bash" (exit 132); JVM misidentifies itself and outputs bash version info.

Root cause: Docker CMD ['/bin/bash', '-c', 'command'] is passed to the entrypoint as $@, then written to the chroot script file via printf '%q ' "$@", producing:

#!/bin/bash
export PATH="..."
/bin/bash -c java\ --version    # ← extra bash layer

This nested bash -c creates an intermediate process whose /proc/self/exe is /bin/bash, which propagates to runtimes checking their process identity.

Fix in entrypoint.sh:

  • Detect the standard Docker CMD pattern ($1=/bin/bash, $2=-c, $#=3)
  • Write $3 (the command string) directly into the script file, eliminating the nested bash layer
  • Fall back to original printf '%q' behavior for non-standard invocations
# Before: nested bash -c wrapper
printf '%q ' "$@" >> "/host${SCRIPT_FILE}"

# After: direct command injection for standard Docker CMD pattern
if [ "$1" = "/bin/bash" ] && [ "$2" = "-c" ] && [ $# -eq 3 ] && [ -n "$3" ]; then
    printf '%s\n' "$3" >> "/host${SCRIPT_FILE}"
else
    printf '%q ' "$@" >> "/host${SCRIPT_FILE}"
fi
  • Updated docs/chroot-mode.md to document the direct-write approach as part of the procfs resolution chain
Original prompt

This section details on the original issue you should resolve

<issue_title>Java and .NET runtimes fail to execute inside AWF chroot mode (v0.13.14)</issue_title>
<issue_description>## Summary

Java and .NET runtimes are completely non-functional inside the AWF agent container in chroot mode. All 20 repos tested (10 Java, 10 .NET) failed with 0 successful builds and 0 tests run. This was discovered during a large-scale build/test experiment across 98 OSS repositories spanning 10 programming languages.

Environment

  • gh-aw: v0.43.2
  • AWF: v0.13.14
  • Runner: ubuntu-latest (GitHub Actions)
  • Chroot mode: enabled (--enable-chroot)

.NET Failure

All 10 .NET repos failed identically. The dotnet CLI refuses to execute because it detects a process name mismatch:

Error: cannot execute dotnet when renamed to bash.
exit code: 132

The .NET CLR validates that its hosting process name matches "dotnet". Inside the AWF chroot execution chain (chroot → bash → capsh → bash → exec), the process name resolves to "bash" instead of "dotnet", triggering this hard security check.

Repos tested: BenchmarkDotNet, Bogus, Dapper, FluentValidation, Humanizer, Newtonsoft.Json, Polly, moq4, serilog, xunit

The Copilot agent attempted multiple workarounds including direct execution, wrapper scripts, Python subprocess, C wrappers, symlinks, and setsid — none could bypass the .NET SDK's process name validation.

Java Failure

All 10 Java repos failed identically. The Java binary outputs bash version information instead of executing Java commands. Maven and Gradle builds fail because the JVM cannot properly initialize.

Repos tested: caffeine, gson, guava, jackson-databind, mapstruct, mockito, mybatis-3, okhttp, resilience4j, retrofit

The root cause appears to be the same /proc/self/exe resolution issue — the JVM reads /proc/self/exe and finds /bin/bash instead of the Java binary, causing it to behave as a shell rather than a JVM.

Context

This was part of a build/test experiment across 10 languages (98 repos total). For comparison:

  • Go: 8/10 repos built and tested successfully
  • Python: 8/10 repos built successfully
  • Rust: 3/10 fully passed (others hit separate linker/toolchain issues)
  • JavaScript: 9/10 pipelines green
  • Java: 0/10 — completely broken
  • .NET: 0/10 — completely broken

The procfs mounting fix introduced in v0.13.13 (container-scoped mount -t proc at /host/proc) does not resolve these failures in v0.13.14.

Reproduction

  1. Fork any Java or .NET repo (e.g., google/gson or dotnet/BenchmarkDotNet)
  2. Add an agentic workflow with engine: copilot and network.allowed: [defaults, java] or [defaults, dotnet]
  3. Compile with gh aw compile
  4. Trigger the workflow
  5. Observe the agent fails to execute java, mvn, gradle, or dotnet commands inside the AWF container</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@Mossaka Mossaka marked this pull request as ready for review February 12, 2026 05:39
Copilot AI review requested due to automatic review settings February 12, 2026 05:39
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot wasn't able to review any files in this pull request.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 12, 2026

🎬 THE ENDSmoke Claude MISSION: ACCOMPLISHED! The hero saves the day! ✨

@github-actions
Copy link
Contributor

github-actions bot commented Feb 12, 2026

📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤

@github-actions github-actions bot mentioned this pull request Feb 12, 2026
@github-actions
Copy link
Contributor

Smoke Test Results

Last 2 Merged PRs:

✅ GitHub MCP: Fetched recent PRs
✅ Playwright: Page title verified ("GitHub · Change is constant. GitHub keeps you ahead. · GitHub")
✅ File Write: Test file created
✅ Bash: File verified

Overall Status: PASS

AI generated by Smoke Claude

@github-actions
Copy link
Contributor

C++ Build Test Results

Project CMake Build Status
fmt PASS
json PASS

Overall: PASS

All C++ projects built successfully.

AI generated by Build Test C++

@github-actions
Copy link
Contributor

Build Test: Bun - ✅ PASS

Test Results

Project Install Tests Status
elysia 1/1 PASS
hono 1/1 PASS

Overall: PASS

All Bun projects built and tested successfully.

AI generated by Build Test Bun

@github-actions
Copy link
Contributor

✅ GitHub MCP: PR #197 (release highlights), PR #706 (hide mcp-logs)
✅ Playwright: GitHub.com title verified
✅ File creation: /tmp/gh-aw/agent/smoke-test-copilot-21934568457.txt
✅ Bash verification: File readable

Status: PASS

cc @Mossaka @Copilot

AI generated by Smoke Copilot

@github-actions
Copy link
Contributor

Go Build Test Results

Project Download Tests Status
color 1/1 PASS
env 1/1 PASS
uuid 1/1 PASS

Overall: PASS

All Go projects successfully downloaded dependencies and passed their tests.

AI generated by Build Test Go

@github-actions
Copy link
Contributor

Deno Build Test Results

Project Tests Status
oak 1/1 ✅ PASS
std 1/1 ✅ PASS

Overall: ✅ PASS

All Deno tests completed successfully.

AI generated by Build Test Deno

@github-actions
Copy link
Contributor

Build Test: Node.js - Results

Project Install Tests Status
clsx PASS PASS
execa PASS PASS
p-limit PASS PASS

Overall: PASS

All Node.js projects built and tested successfully.

AI generated by Build Test Node.js

@github-actions
Copy link
Contributor

Rust Build Test Results

Project Build Tests Status
fd 1/1 PASS
zoxide 1/1 PASS

Overall: PASS

All Rust projects built and tested successfully.

AI generated by Build Test Rust

@github-actions
Copy link
Contributor

Java Build Test Results

Project Compile Tests Status
gson 1/1 PASS
caffeine 1/1 PASS

Overall: PASS

All Java projects successfully compiled and passed their test suites through the AWF firewall with Maven proxy configuration.

AI generated by Build Test Java

Copilot AI changed the title [WIP] Fix Java and .NET runtimes execution in AWF chroot mode fix: eliminate nested bash layer in chroot command execution for Java/.NET Feb 12, 2026
Copilot AI requested a review from Mossaka February 12, 2026 05:52
@github-actions
Copy link
Contributor

Java Build Test Results

Project Compile Tests Status
gson 1/1 PASS
caffeine 1/1 PASS

Overall: PASS

All Java projects compiled and tested successfully through AWF firewall with Maven proxy configuration.

AI generated by Build Test Java

@github-actions
Copy link
Contributor

Chroot Version Comparison Test Results

Runtime Host Version Chroot Version Match?
Python 3.12.12 3.12.3 ❌ NO
Node.js v24.13.0 v20.20.0 ❌ NO
Go go1.22.12 go1.22.12 ✅ YES

Overall Result: Tests FAILED (not all versions match)

The chroot environment successfully accessed host binaries, but version mismatches were detected for Python and Node.js. Go versions matched correctly.

AI generated by Smoke Chroot

@github-actions
Copy link
Contributor

Smoke Test Results

Last 2 Merged PRs:

Test Results:

  • ✅ GitHub MCP: Retrieved merged PRs successfully
  • ✅ Playwright: Navigated to github.com, page title contains "GitHub"
  • ✅ File Writing: Created /tmp/gh-aw/agent/smoke-test-copilot-21961398432.txt
  • ✅ Bash Tool: Verified file content successfully

Overall Status: PASS 🎉

cc @Mossaka @Copilot

AI generated by Smoke Copilot

@Mossaka Mossaka merged commit 2768515 into main Feb 12, 2026
93 checks passed
@Mossaka Mossaka deleted the copilot/fix-java-dotnet-chroot branch February 12, 2026 19:56
@github-actions github-actions bot mentioned this pull request Feb 13, 2026
Mossaka added a commit that referenced this pull request Mar 11, 2026
Add integration tests verifying the capsh execution chain works correctly
after PR #715 eliminated the nested bash layer for Java/.NET compatibility.

Tests verify:
- CAP_NET_ADMIN, CAP_SYS_CHROOT, CAP_SYS_ADMIN dropped from CapBnd bitmask
- iptables, chroot, mount commands fail (capabilities enforced)
- Commands run under bash shell (BASH_VERSION set)
- /proc/self/exe resolves correctly for python3 (not /bin/bash)
- Special characters and pipe chains work with direct-write approach

Fixes #842

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mossaka added a commit that referenced this pull request Mar 11, 2026
Add integration tests verifying the capsh execution chain works correctly
after PR #715 eliminated the nested bash layer for Java/.NET compatibility.

Tests verify:
- CAP_NET_ADMIN, CAP_SYS_CHROOT, CAP_SYS_ADMIN dropped from CapBnd bitmask
- iptables, chroot, mount commands fail (capabilities enforced)
- Commands run under bash shell (BASH_VERSION set)
- /proc/self/exe resolves correctly for python3 (not /bin/bash)
- Special characters and pipe chains work with direct-write approach

Fixes #842

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Java and .NET runtimes fail to execute inside AWF chroot mode (v0.13.14)

3 participants