Skip to content

Commit 81a5805

Browse files
lpcoxCopilotCopilot
authored
fix: add size-based filtering to --env-all to prevent E2BIG (#1978)
* 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> * Update src/docker-manager.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 06b99eb commit 81a5805

2 files changed

Lines changed: 80 additions & 0 deletions

File tree

src/docker-manager.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1645,6 +1645,42 @@ describe('docker-manager', () => {
16451645
}
16461646
});
16471647

1648+
it('should skip env vars exceeding MAX_ENV_VALUE_SIZE from env-all passthrough', () => {
1649+
const largeVarName = 'AWF_TEST_OVERSIZED_VAR';
1650+
const saved = process.env[largeVarName];
1651+
// Create a value larger than 64KB
1652+
process.env[largeVarName] = 'x'.repeat(65 * 1024);
1653+
1654+
try {
1655+
const configWithEnvAll = { ...mockConfig, envAll: true };
1656+
const result = generateDockerCompose(configWithEnvAll, mockNetworkConfig);
1657+
const env = result.services.agent.environment as Record<string, string>;
1658+
1659+
// Oversized var should be skipped
1660+
expect(env[largeVarName]).toBeUndefined();
1661+
} finally {
1662+
if (saved !== undefined) process.env[largeVarName] = saved;
1663+
else delete process.env[largeVarName];
1664+
}
1665+
});
1666+
1667+
it('should pass env vars under MAX_ENV_VALUE_SIZE from env-all passthrough', () => {
1668+
const normalVarName = 'AWF_TEST_NORMAL_VAR';
1669+
const saved = process.env[normalVarName];
1670+
process.env[normalVarName] = 'normal_value';
1671+
1672+
try {
1673+
const configWithEnvAll = { ...mockConfig, envAll: true };
1674+
const result = generateDockerCompose(configWithEnvAll, mockNetworkConfig);
1675+
const env = result.services.agent.environment as Record<string, string>;
1676+
1677+
expect(env[normalVarName]).toBe('normal_value');
1678+
} finally {
1679+
if (saved !== undefined) process.env[normalVarName] = saved;
1680+
else delete process.env[normalVarName];
1681+
}
1682+
});
1683+
16481684
it('should auto-inject GH_HOST from GITHUB_SERVER_URL when envAll is true', () => {
16491685
const prevServerUrl = process.env.GITHUB_SERVER_URL;
16501686
const prevGhHost = process.env.GH_HOST;

src/docker-manager.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,19 @@ const API_PROXY_CONTAINER_NAME = 'awf-api-proxy';
2424
const DOH_PROXY_CONTAINER_NAME = 'awf-doh-proxy';
2525
const CLI_PROXY_CONTAINER_NAME = 'awf-cli-proxy';
2626

27+
/**
28+
* Maximum size (bytes) of a single environment variable value allowed through
29+
* --env-all passthrough. Variables exceeding this are skipped with a warning
30+
* to prevent E2BIG errors from ARG_MAX exhaustion.
31+
*/
32+
const MAX_ENV_VALUE_SIZE = 64 * 1024; // 64 KB
33+
34+
/**
35+
* Total environment size (bytes) threshold for issuing an ARG_MAX warning.
36+
* Linux ARG_MAX is ~2 MB for argv + envp combined; warn well before that.
37+
*/
38+
const ENV_SIZE_WARNING_THRESHOLD = 1_500_000; // ~1.5 MB
39+
2740
/**
2841
* Flag set by fastKillAgentContainer() to signal runAgentCommand() that
2942
* the container was externally stopped. When true, runAgentCommand() skips
@@ -880,11 +893,27 @@ export function generateDockerCompose(
880893

881894
// If --env-all is specified, pass through all host environment variables (except excluded ones)
882895
if (config.envAll) {
896+
const skippedLargeVars: string[] = [];
883897
for (const [key, value] of Object.entries(process.env)) {
884898
if (value !== undefined && !EXCLUDED_ENV_VARS.has(key) && !Object.prototype.hasOwnProperty.call(environment, key)) {
899+
// Skip oversized values to prevent E2BIG (Argument list too long) errors.
900+
// The Linux kernel enforces ARG_MAX (~2MB) on argv+envp combined; large env
901+
// vars can exhaust this budget, especially when combined with large prompts.
902+
const valueSizeBytes = Buffer.byteLength(value, 'utf8');
903+
if (valueSizeBytes > MAX_ENV_VALUE_SIZE) {
904+
skippedLargeVars.push(`${key} (${(valueSizeBytes / 1024).toFixed(0)} KB)`);
905+
continue;
906+
}
885907
environment[key] = value;
886908
}
887909
}
910+
if (skippedLargeVars.length > 0) {
911+
logger.warn(`Skipped ${skippedLargeVars.length} oversized env var(s) from --env-all passthrough (>${(MAX_ENV_VALUE_SIZE / 1024).toFixed(0)} KB each):`);
912+
for (const entry of skippedLargeVars) {
913+
logger.warn(` - ${entry}`);
914+
}
915+
logger.warn('Use --env VAR="$VAR" to explicitly pass large values if needed.');
916+
}
888917
} else {
889918
// Default behavior: selectively pass through specific variables
890919
if (process.env.GITHUB_TOKEN) environment.GITHUB_TOKEN = process.env.GITHUB_TOKEN;
@@ -989,6 +1018,21 @@ export function generateDockerCompose(
9891018
}
9901019
}
9911020

1021+
// Warn when total environment size approaches ARG_MAX (~2MB).
1022+
// Linux enforces a combined argv+envp limit; large environments can cause E2BIG errors
1023+
// when execve() is called inside the container.
1024+
if (config.envAll) {
1025+
const totalEnvBytes = Object.entries(environment)
1026+
.reduce((sum, [k, v]) => sum + k.length + (v?.length ?? 0) + 2, 0); // +2 for '=' and null
1027+
if (totalEnvBytes > ENV_SIZE_WARNING_THRESHOLD) {
1028+
logger.warn(
1029+
`⚠️ Total container environment size is ${(totalEnvBytes / 1024).toFixed(0)} KB — ` +
1030+
'may cause E2BIG (Argument list too long) errors when combined with large command arguments'
1031+
);
1032+
logger.warn(' Consider using --exclude-env to remove unnecessary variables');
1033+
}
1034+
}
1035+
9921036
// DNS servers for Docker embedded DNS forwarding (used in docker-compose dns: field)
9931037
const dnsServers = config.dnsServers || DEFAULT_DNS_SERVERS;
9941038
// Pass DNS servers to container so setup-iptables.sh can allow Docker DNS forwarding

0 commit comments

Comments
 (0)