From 9210291a042bb898224c8b7abebef39df6a4fc93 Mon Sep 17 00:00:00 2001 From: Landon Cox Date: Tue, 14 Apr 2026 14:29:48 -0700 Subject: [PATCH 1/2] fix: add size-based filtering to --env-all to prevent E2BIG Add MAX_ENV_VALUE_SIZE (64KB) per-variable cap to --env-all passthrough. Variables exceeding this threshold are skipped with a warning listing the dropped keys and their sizes. Also add a total environment size warning when the combined env approaches ARG_MAX (~1.5MB threshold). This prevents the 'Argument list too long' (E2BIG) kernel error that occurs when the runner environment (~1.5-2MB) is combined with large command arguments (e.g., inlined prompts). Closes #1965 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/docker-manager.test.ts | 36 +++++++++++++++++++++++++++++++ src/docker-manager.ts | 43 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/src/docker-manager.test.ts b/src/docker-manager.test.ts index e3d74ec7..257b3706 100644 --- a/src/docker-manager.test.ts +++ b/src/docker-manager.test.ts @@ -1645,6 +1645,42 @@ describe('docker-manager', () => { } }); + it('should skip env vars exceeding MAX_ENV_VALUE_SIZE from env-all passthrough', () => { + const largeVarName = 'AWF_TEST_OVERSIZED_VAR'; + const saved = process.env[largeVarName]; + // Create a value larger than 64KB + process.env[largeVarName] = 'x'.repeat(65 * 1024); + + try { + const configWithEnvAll = { ...mockConfig, envAll: true }; + const result = generateDockerCompose(configWithEnvAll, mockNetworkConfig); + const env = result.services.agent.environment as Record; + + // Oversized var should be skipped + expect(env[largeVarName]).toBeUndefined(); + } finally { + if (saved !== undefined) process.env[largeVarName] = saved; + else delete process.env[largeVarName]; + } + }); + + it('should pass env vars under MAX_ENV_VALUE_SIZE from env-all passthrough', () => { + const normalVarName = 'AWF_TEST_NORMAL_VAR'; + const saved = process.env[normalVarName]; + process.env[normalVarName] = 'normal_value'; + + try { + const configWithEnvAll = { ...mockConfig, envAll: true }; + const result = generateDockerCompose(configWithEnvAll, mockNetworkConfig); + const env = result.services.agent.environment as Record; + + expect(env[normalVarName]).toBe('normal_value'); + } finally { + if (saved !== undefined) process.env[normalVarName] = saved; + else delete process.env[normalVarName]; + } + }); + it('should auto-inject GH_HOST from GITHUB_SERVER_URL when envAll is true', () => { const prevServerUrl = process.env.GITHUB_SERVER_URL; const prevGhHost = process.env.GH_HOST; diff --git a/src/docker-manager.ts b/src/docker-manager.ts index 802abd61..f9ff64e9 100644 --- a/src/docker-manager.ts +++ b/src/docker-manager.ts @@ -24,6 +24,19 @@ const API_PROXY_CONTAINER_NAME = 'awf-api-proxy'; const DOH_PROXY_CONTAINER_NAME = 'awf-doh-proxy'; const CLI_PROXY_CONTAINER_NAME = 'awf-cli-proxy'; +/** + * Maximum size (bytes) of a single environment variable value allowed through + * --env-all passthrough. Variables exceeding this are skipped with a warning + * to prevent E2BIG errors from ARG_MAX exhaustion. + */ +const MAX_ENV_VALUE_SIZE = 64 * 1024; // 64 KB + +/** + * Total environment size (bytes) threshold for issuing an ARG_MAX warning. + * Linux ARG_MAX is ~2 MB for argv + envp combined; warn well before that. + */ +const ENV_SIZE_WARNING_THRESHOLD = 1_500_000; // ~1.5 MB + /** * Flag set by fastKillAgentContainer() to signal runAgentCommand() that * the container was externally stopped. When true, runAgentCommand() skips @@ -794,11 +807,26 @@ export function generateDockerCompose( // If --env-all is specified, pass through all host environment variables (except excluded ones) if (config.envAll) { + const skippedLargeVars: string[] = []; for (const [key, value] of Object.entries(process.env)) { if (value !== undefined && !EXCLUDED_ENV_VARS.has(key) && !Object.prototype.hasOwnProperty.call(environment, key)) { + // Skip oversized values to prevent E2BIG (Argument list too long) errors. + // The Linux kernel enforces ARG_MAX (~2MB) on argv+envp combined; large env + // vars can exhaust this budget, especially when combined with large prompts. + if (value.length > MAX_ENV_VALUE_SIZE) { + skippedLargeVars.push(`${key} (${(value.length / 1024).toFixed(0)} KB)`); + continue; + } environment[key] = value; } } + if (skippedLargeVars.length > 0) { + logger.warn(`Skipped ${skippedLargeVars.length} oversized env var(s) from --env-all passthrough (>${(MAX_ENV_VALUE_SIZE / 1024).toFixed(0)} KB each):`); + for (const entry of skippedLargeVars) { + logger.warn(` - ${entry}`); + } + logger.warn('Use --env VAR="$VAR" to explicitly pass large values if needed.'); + } } else { // Default behavior: selectively pass through specific variables if (process.env.GITHUB_TOKEN) environment.GITHUB_TOKEN = process.env.GITHUB_TOKEN; @@ -903,6 +931,21 @@ export function generateDockerCompose( } } + // Warn when total environment size approaches ARG_MAX (~2MB). + // Linux enforces a combined argv+envp limit; large environments can cause E2BIG errors + // when execve() is called inside the container. + if (config.envAll) { + const totalEnvBytes = Object.entries(environment) + .reduce((sum, [k, v]) => sum + k.length + (v?.length ?? 0) + 2, 0); // +2 for '=' and null + if (totalEnvBytes > ENV_SIZE_WARNING_THRESHOLD) { + logger.warn( + `⚠️ Total container environment size is ${(totalEnvBytes / 1024).toFixed(0)} KB — ` + + 'may cause E2BIG (Argument list too long) errors when combined with large command arguments' + ); + logger.warn(' Consider using --exclude-env to remove unnecessary variables'); + } + } + // DNS servers for Docker embedded DNS forwarding (used in docker-compose dns: field) const dnsServers = config.dnsServers || DEFAULT_DNS_SERVERS; // Pass DNS servers to container so setup-iptables.sh can allow Docker DNS forwarding From 3c8489a164504acbfcdf5f0807a0c8d14cc3f9df Mon Sep 17 00:00:00 2001 From: Landon Cox Date: Tue, 14 Apr 2026 14:33:05 -0700 Subject: [PATCH 2/2] Update src/docker-manager.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/docker-manager.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/docker-manager.ts b/src/docker-manager.ts index f9ff64e9..b6582e71 100644 --- a/src/docker-manager.ts +++ b/src/docker-manager.ts @@ -813,8 +813,9 @@ export function generateDockerCompose( // Skip oversized values to prevent E2BIG (Argument list too long) errors. // The Linux kernel enforces ARG_MAX (~2MB) on argv+envp combined; large env // vars can exhaust this budget, especially when combined with large prompts. - if (value.length > MAX_ENV_VALUE_SIZE) { - skippedLargeVars.push(`${key} (${(value.length / 1024).toFixed(0)} KB)`); + const valueSizeBytes = Buffer.byteLength(value, 'utf8'); + if (valueSizeBytes > MAX_ENV_VALUE_SIZE) { + skippedLargeVars.push(`${key} (${(valueSizeBytes / 1024).toFixed(0)} KB)`); continue; } environment[key] = value;