Skip to content

Commit 2dd11e9

Browse files
ekzhusonichi
authored andcommitted
[Core] Sanitize filename before using it as docker image tag. Fix #1069 (#1127)
* fix #1069 * uncomment skips * use importlib to check package * better code --------- Co-authored-by: Chi Wang <[email protected]>
1 parent 3320f80 commit 2dd11e9

File tree

2 files changed

+58
-10
lines changed

2 files changed

+58
-10
lines changed

autogen/code_utils.py

+26-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import os
33
import pathlib
44
import re
5+
import string
56
import subprocess
67
import sys
78
import time
@@ -224,6 +225,29 @@ def _cmd(lang):
224225
raise NotImplementedError(f"{lang} not recognized in code execution")
225226

226227

228+
def _sanitize_filename_for_docker_tag(filename: str) -> str:
229+
"""Convert a filename to a valid docker tag.
230+
See https://docs.docker.com/engine/reference/commandline/tag/ for valid tag
231+
format.
232+
233+
Args:
234+
filename (str): The filename to be converted.
235+
236+
Returns:
237+
str: The sanitized Docker tag.
238+
"""
239+
# Replace any character not allowed with an underscore
240+
allowed_chars = set(string.ascii_letters + string.digits + "_.-")
241+
sanitized = "".join(char if char in allowed_chars else "_" for char in filename)
242+
243+
# Ensure it does not start with a period or a dash
244+
if sanitized.startswith(".") or sanitized.startswith("-"):
245+
sanitized = "_" + sanitized[1:]
246+
247+
# Truncate if longer than 128 characters
248+
return sanitized[:128]
249+
250+
227251
def execute_code(
228252
code: Optional[str] = None,
229253
timeout: Optional[int] = None,
@@ -379,7 +403,7 @@ def execute_code(
379403
cmd = [
380404
"sh",
381405
"-c",
382-
f"{_cmd(lang)} {filename}; exit_code=$?; echo -n {exit_code_str}; echo -n $exit_code; echo {exit_code_str}",
406+
f'{_cmd(lang)} "{filename}"; exit_code=$?; echo -n {exit_code_str}; echo -n $exit_code; echo {exit_code_str}',
383407
]
384408
# create a docker container
385409
container = client.containers.run(
@@ -403,7 +427,7 @@ def execute_code(
403427
# get the container logs
404428
logs = container.logs().decode("utf-8").rstrip()
405429
# commit the image
406-
tag = filename.replace("/", "")
430+
tag = _sanitize_filename_for_docker_tag(filename)
407431
container.commit(repository="python", tag=tag)
408432
# remove the container
409433
container.remove()

test/test_code.py

+32-8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import importlib.metadata
12
import os
23
import sys
34
import unittest
@@ -22,6 +23,21 @@
2223
here = os.path.abspath(os.path.dirname(__file__))
2324

2425

26+
def is_package_installed(package_name):
27+
"""Check if a package is installed. This is a preferred way to check if a
28+
package is installed or not than doing a try-catch around an import
29+
because it avoids name conflict with local modules and
30+
code execution in the imported module."""
31+
try:
32+
importlib.metadata.version(package_name)
33+
return True
34+
except importlib.metadata.PackageNotFoundError:
35+
return False
36+
37+
38+
docker_package_installed = is_package_installed("docker")
39+
40+
2541
# def test_find_code():
2642
# try:
2743
# import openai
@@ -293,10 +309,6 @@ def scrape(url):
293309
assert len(codeblocks) == 1 and codeblocks[0] == ("", "source setup.sh")
294310

295311

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

340352

353+
@pytest.mark.skipif(docker_package_installed is False, reason="docker package not installed")
354+
def test_execute_code_with_custom_filename_on_docker():
355+
exit_code, msg, image = execute_code("print('hello world')", filename="tmp/codetest.py", use_docker=True)
356+
assert exit_code == 0 and msg == "hello world\n", msg
357+
assert image == "python:tmp_codetest.py"
358+
359+
360+
@pytest.mark.skipif(docker_package_installed is False, reason="docker package not installed")
361+
def test_execute_code_with_misformed_filename_on_docker():
362+
exit_code, msg, image = execute_code(
363+
"print('hello world')", filename="tmp/codetest.py (some extra information)", use_docker=True
364+
)
365+
assert exit_code == 0 and msg == "hello world\n", msg
366+
assert image == "python:tmp_codetest.py__some_extra_information_"
367+
368+
341369
def test_execute_code_raises_when_code_and_filename_are_both_none():
342370
with pytest.raises(AssertionError):
343371
execute_code(code=None, filename=None)
344372

345373

346-
@pytest.mark.skipif(
347-
sys.platform in ["darwin"],
348-
reason="do not run on MacOS",
349-
)
350374
def test_execute_code_nodocker():
351375
test_execute_code(use_docker=False)
352376

0 commit comments

Comments
 (0)