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

[Core] Sanitize filename before using it as docker image tag. Fix #1069 #1127

Merged
merged 8 commits into from
Jan 13, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
27 changes: 25 additions & 2 deletions autogen/code_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,29 @@ def _cmd(lang):
raise NotImplementedError(f"{lang} not recognized in code execution")


def _sanitize_filename_for_docker_tag(filename: str) -> str:
"""Convert a filename to a valid docker tag.
See https://docs.docker.com/engine/reference/commandline/tag/ for valid tag
format.

Args:
filename (str): The filename to be converted.

Returns:
str: The sanitized Docker tag.
"""
# Replace any character not allowed with an underscore
allowed_chars = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.-")
ekzhu marked this conversation as resolved.
Show resolved Hide resolved
sanitized = "".join(char if char in allowed_chars else "_" for char in filename)

# Ensure it does not start with a period or a dash
if sanitized.startswith(".") or sanitized.startswith("-"):
sanitized = sanitized.replace(".", "_", 1).replace("-", "_", 1)
ekzhu marked this conversation as resolved.
Show resolved Hide resolved

# Truncate if longer than 128 characters
return sanitized[:128]


def execute_code(
code: Optional[str] = None,
timeout: Optional[int] = None,
Expand Down Expand Up @@ -379,7 +402,7 @@ def execute_code(
cmd = [
"sh",
"-c",
f"{_cmd(lang)} {filename}; exit_code=$?; echo -n {exit_code_str}; echo -n $exit_code; echo {exit_code_str}",
f'{_cmd(lang)} "{filename}"; exit_code=$?; echo -n {exit_code_str}; echo -n $exit_code; echo {exit_code_str}',
]
# create a docker container
container = client.containers.run(
Expand All @@ -403,7 +426,7 @@ def execute_code(
# get the container logs
logs = container.logs().decode("utf-8").rstrip()
# commit the image
tag = filename.replace("/", "")
tag = _sanitize_filename_for_docker_tag(filename)
container.commit(repository="python", tag=tag)
# remove the container
container.remove()
Expand Down
40 changes: 32 additions & 8 deletions test/test_code.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import importlib.metadata
import os
import sys
import unittest
Expand All @@ -22,6 +23,21 @@
here = os.path.abspath(os.path.dirname(__file__))


def is_package_installed(package_name):
"""Check if a package is installed. This is a preferred way to check if a
package is installed or not than doing a try-catch around an import
because it avoids name conflict with local modules and
code execution in the imported module."""
try:
importlib.metadata.version(package_name)
return True
except importlib.metadata.PackageNotFoundError:
return False


docker_package_installed = is_package_installed("docker")


# def test_find_code():
# try:
# import openai
Expand Down Expand Up @@ -293,10 +309,6 @@ def scrape(url):
assert len(codeblocks) == 1 and codeblocks[0] == ("", "source setup.sh")


@pytest.mark.skipif(
sys.platform in ["darwin"],
reason="do not run on MacOS",
)
def test_execute_code(use_docker=None):
try:
import docker
Expand Down Expand Up @@ -338,15 +350,27 @@ def test_execute_code(use_docker=None):
assert isinstance(image, str) or docker is None or os.path.exists("/.dockerenv") or use_docker is False


@pytest.mark.skipif(docker_package_installed is False, reason="docker package not installed")
def test_execute_code_with_custom_filename_on_docker():
exit_code, msg, image = execute_code("print('hello world')", filename="tmp/codetest.py", use_docker=True)
assert exit_code == 0 and msg == "hello world\n", msg
assert image == "python:tmp_codetest.py"


@pytest.mark.skipif(docker_package_installed is False, reason="docker package not installed")
def test_execute_code_with_misformed_filename_on_docker():
exit_code, msg, image = execute_code(
"print('hello world')", filename="tmp/codetest.py (some extra information)", use_docker=True
)
assert exit_code == 0 and msg == "hello world\n", msg
assert image == "python:tmp_codetest.py__some_extra_information_"
ekzhu marked this conversation as resolved.
Show resolved Hide resolved


def test_execute_code_raises_when_code_and_filename_are_both_none():
with pytest.raises(AssertionError):
execute_code(code=None, filename=None)


@pytest.mark.skipif(
sys.platform in ["darwin"],
reason="do not run on MacOS",
)
def test_execute_code_nodocker():
test_execute_code(use_docker=False)

Expand Down
Loading