Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Command line code sanitation #1627

Merged
merged 21 commits into from
Feb 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions autogen/coding/local_commandline_code_executor.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import re
import uuid
import warnings
from typing import Any, ClassVar, List, Optional
Expand Down Expand Up @@ -43,6 +44,8 @@ class LocalCommandlineCodeExecutor(BaseModel):
the working directory, and a unique file is generated and saved in the
working directory for each code block.
The code blocks are executed in the order they are received.
Command line code is sanitized using regular expression match against a list of dangerous commands in order to prevent self-destructive
commands from being executed which may potentially affect the users environment.
Currently the only supported languages is Python and shell scripts.
For Python code, use the language "python" for the code block.
For shell scripts, use the language "bash", "shell", or "sh" for the code
Expand Down Expand Up @@ -108,6 +111,28 @@ def code_extractor(self) -> CodeExtractor:
"""(Experimental) Export a code extractor that can be used by an agent."""
return MarkdownCodeExtractor()

@staticmethod
def sanitize_command(lang: str, code: str) -> None:
"""
Sanitize the code block to prevent dangerous commands.
This approach acknowledges that while Docker or similar
containerization/sandboxing technologies provide a robust layer of security,
not all users may have Docker installed or may choose not to use it.
Therefore, having a baseline level of protection helps mitigate risks for users who,
either out of choice or necessity, run code outside of a sandboxed environment.
"""
dangerous_patterns = [
(r"\brm\s+-rf\b", "Use of 'rm -rf' command is not allowed."),
(r"\bmv\b.*?\s+/dev/null", "Moving files to /dev/null is not allowed."),
(r"\bdd\b", "Use of 'dd' command is not allowed."),
(r">\s*/dev/sd[a-z][1-9]?", "Overwriting disk blocks directly is not allowed."),
(r":\(\)\{\s*:\|\:&\s*\};:", "Fork bombs are not allowed."),
]
if lang in ["bash", "shell", "sh"]:
for pattern, message in dangerous_patterns:
if re.search(pattern, code):
raise ValueError(f"Potentially dangerous command detected: {message}")

def execute_code_blocks(self, code_blocks: List[CodeBlock]) -> CommandlineCodeResult:
"""(Experimental) Execute the code blocks and return the result.

Expand All @@ -119,6 +144,9 @@ def execute_code_blocks(self, code_blocks: List[CodeBlock]) -> CommandlineCodeRe
logs_all = ""
for i, code_block in enumerate(code_blocks):
lang, code = code_block.language, code_block.code

LocalCommandlineCodeExecutor.sanitize_command(lang, code)

print(
colored(
f"\n>>>>>>>> EXECUTING CODE BLOCK {i} (inferred language is {lang})...",
Expand Down
19 changes: 19 additions & 0 deletions test/coding/test_commandline_code_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,22 @@ def _test_conversable_agent_code_execution(executor: CodeExecutor) -> None:
sender=ConversableAgent("user", llm_config=False, code_execution_config=False),
)
assert "hello extract code" in reply # type: ignore[operator]


# Test cases for dangerous commands that should be caught by the sanitizer
@pytest.mark.parametrize(
"lang, code, expected_message",
[
("bash", "rm -rf /", "Use of 'rm -rf' command is not allowed."),
("bash", "mv myFile /dev/null", "Moving files to /dev/null is not allowed."),
("bash", "dd if=/dev/zero of=/dev/sda", "Use of 'dd' command is not allowed."),
("bash", "echo Hello > /dev/sda", "Overwriting disk blocks directly is not allowed."),
("bash", ":(){ :|:& };:", "Fork bombs are not allowed."),
],
)
def test_dangerous_commands(lang, code, expected_message):
with pytest.raises(ValueError) as exc_info:
LocalCommandlineCodeExecutor.sanitize_command(lang, code)
assert expected_message in str(
exc_info.value
), f"Expected message '{expected_message}' not found in '{str(exc_info.value)}'"
File renamed without changes.
Loading