MCPcopy Create free account
hub / github.com/langchain-ai/deepagents / is_shell_command_allowed

Function is_shell_command_allowed

libs/code/deepagents_code/config.py:2785–2850  ·  view source on GitHub ↗

Check if a shell command is in the allow-list. The allow-list matches against the first token of the command (the executable name). This allows read-only commands like ls, cat, grep, etc. to be auto-approved. When `allow_list` is the `SHELL_ALLOW_ALL` sentinel, all non-empty comman

(command: str, allow_list: list[str] | None)

Source from the content-addressed store, hash-verified

2783
2784
2785def is_shell_command_allowed(command: str, allow_list: list[str] | None) -> bool:
2786 """Check if a shell command is in the allow-list.
2787
2788 The allow-list matches against the first token of the command (the executable
2789 name). This allows read-only commands like ls, cat, grep, etc. to be
2790 auto-approved.
2791
2792 When `allow_list` is the `SHELL_ALLOW_ALL` sentinel, all non-empty commands
2793 are approved unconditionally — dangerous pattern checks are skipped.
2794
2795 SECURITY: For regular allow-lists, this function rejects commands containing
2796 dangerous shell patterns (command substitution, redirects, process
2797 substitution, etc.) BEFORE parsing, to prevent injection attacks that could
2798 bypass the allow-list.
2799
2800 Args:
2801 command: The full shell command to check.
2802 allow_list: List of allowed command names (e.g., `["ls", "cat", "grep"]`),
2803 the `SHELL_ALLOW_ALL` sentinel to allow any command, or `None`.
2804
2805 Returns:
2806 `True` if the command is allowed, `False` otherwise.
2807 """
2808 if not allow_list or not command or not command.strip():
2809 return False
2810
2811 # SHELL_ALLOW_ALL sentinel — skip pattern and token checks
2812 if isinstance(allow_list, _ShellAllowAll):
2813 return True
2814
2815 # SECURITY: Check for dangerous patterns BEFORE any parsing
2816 # This prevents injection attacks like: ls "$(rm -rf /)"
2817 if contains_dangerous_patterns(command):
2818 return False
2819
2820 allow_set = set(allow_list)
2821
2822 # Extract the first command token
2823 # Handle pipes and other shell operators by checking each command in the pipeline
2824 # Split by compound operators first (&&, ||), then single-char operators (|, ;).
2825 # Note: standalone & (background) is blocked by contains_dangerous_patterns above.
2826 segments = re.split(r"&&|\|\||[|;]", command)
2827
2828 # Track if we found at least one valid command
2829 found_command = False
2830
2831 for raw_segment in segments:
2832 segment = raw_segment.strip()
2833 if not segment:
2834 continue
2835
2836 try:
2837 # Try to parse as shell command to extract the executable name
2838 tokens = shlex.split(segment)
2839 if tokens:
2840 found_command = True
2841 cmd_name = tokens[0]
2842 # Check if this command is in the allow set

Calls 1