test(docker): verify capsh execution chain after PR #715#1240
Conversation
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>
✅ Coverage Check PassedOverall Coverage
📁 Per-file Coverage Changes (1 files)
Coverage comparison generated by |
There was a problem hiding this comment.
Pull request overview
Adds an integration test to validate the post–PR #715 chroot execution chain (capsh capability dropping + command execution semantics), guarding against regressions in how commands are written/executed inside the chroot script.
Changes:
- Introduces a new batched integration test suite that inspects CapBnd and attempts privileged operations (iptables/chroot/mount) to confirm capabilities are dropped.
- Adds checks around bash execution,
/proc/self/exeresolution, and special-character/pipeline handling under the direct-write approach.
Comments suppressed due to low confidence (1)
tests/integration/chroot-capsh-chain.test.ts:55
- The
; echo "exit=$?"suffix makes the batch wrapper’s capturedexitCodealways 0 (because the subshell’s last command isecho). This also allows false positives (e.g.,iptablesmissing yields a non-zero exit that still looks like a capability drop). Prefer running the command directly and asserting onBatchCommandResult.exitCode(and optionally matching stderr/stdout forOperation not permitted/permission deniedto ensure the failure is actually capability-related).
{ name: 'iptables_blocked', command: 'iptables -L 2>&1; echo "exit=$?"' },
// Verify CAP_SYS_CHROOT (bit 18) is dropped in chroot mode
{ name: 'chroot_blocked', command: 'chroot / /bin/true 2>&1; echo "exit=$?"' },
// Verify CAP_SYS_ADMIN (bit 21) is dropped - mount should fail
{ name: 'mount_blocked', command: 'mount -t tmpfs tmpfs /tmp/test-mount 2>&1; echo "exit=$?"' },
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Verify CAP_SYS_ADMIN (bit 21) is dropped - mount should fail | ||
| { name: 'mount_blocked', command: 'mount -t tmpfs tmpfs /tmp/test-mount 2>&1; echo "exit=$?"' }, |
There was a problem hiding this comment.
mount -t tmpfs tmpfs /tmp/test-mount can fail for reasons unrelated to CAP_SYS_ADMIN (e.g., missing mountpoint), and this test currently only checks exit=.... Create a mountpoint first (e.g., under mktemp -d), attempt the mount, and assert the failure mode is permission-related; also ensure any created directory is cleaned up.
| // Verify CAP_SYS_ADMIN (bit 21) is dropped - mount should fail | |
| { name: 'mount_blocked', command: 'mount -t tmpfs tmpfs /tmp/test-mount 2>&1; echo "exit=$?"' }, | |
| // Verify CAP_SYS_ADMIN (bit 21) is dropped - mount should fail due to permissions, not missing mountpoint | |
| { name: 'mount_blocked', command: 'mount_dir="$(mktemp -d)"; mount -t tmpfs tmpfs "$mount_dir" 2>&1; status="$?"; rmdir "$mount_dir"; echo "exit=$status"' }, |
| // Verify that the process tree doesn't have an extra bash layer | ||
| // ps should show bash -> capsh -> bash -> command, NOT bash -> capsh -> bash -> bash -> command | ||
| { name: 'process_tree', command: 'ps -o comm= --ppid $PPID 2>/dev/null || ps -o comm= $PPID 2>/dev/null || echo "ps_unavailable"' }, |
There was a problem hiding this comment.
process_tree is executed in the batch but never asserted on, so it adds runtime/noise without increasing coverage. Either add a test that validates the expected chain (no extra nested bash), or remove this batch command to keep the invocation minimal.
| // Verify that the process tree doesn't have an extra bash layer | |
| // ps should show bash -> capsh -> bash -> command, NOT bash -> capsh -> bash -> bash -> command | |
| { name: 'process_tree', command: 'ps -o comm= --ppid $PPID 2>/dev/null || ps -o comm= $PPID 2>/dev/null || echo "ps_unavailable"' }, |
| if (r.exitCode === 0) { | ||
| // python3's /proc/self/exe should point to python, not bash | ||
| expect(r.stdout).toMatch(/python/); | ||
| expect(r.stdout).not.toMatch(/\/bin\/bash$/); | ||
| } | ||
| // Skip if python3 not available | ||
| }); | ||
|
|
There was a problem hiding this comment.
This test silently passes if python3 is unavailable (if (r.exitCode === 0) { ... }). Other integration tests in this suite appear to assume python3 exists (and would fail if it didn’t), so this weakens the signal when running this test file in isolation. Consider asserting r.exitCode === 0 (or explicitly test.skip with a clear condition) so missing python fails loudly.
This issue also appears on line 51 of the same file.
| if (r.exitCode === 0) { | |
| // python3's /proc/self/exe should point to python, not bash | |
| expect(r.stdout).toMatch(/python/); | |
| expect(r.stdout).not.toMatch(/\/bin\/bash$/); | |
| } | |
| // Skip if python3 not available | |
| }); | |
| expect(r.exitCode).toBe(0); | |
| // python3's /proc/self/exe should point to python, not bash | |
| expect(r.stdout).toMatch(/python/); | |
| expect(r.stdout).not.toMatch(/\/bin\/bash$/); | |
| }); |
Smoke Test Results✅ GitHub MCP: #1229 feat(cli): add short flags for frequently used options, #1228 docs: clarify --image-tag behavior with agent-image presets Overall: PASS
|
|
Smoke test results for
Overall: PASS
|
|
PRs: feat(cli): add short flags for frequently used options; docs: clarify --image-tag behavior with agent-image presets
|
Summary
Test plan
Fixes #842
🤖 Generated with Claude Code