Skip to content

Bubblewrap

minisweagent.environments.extra.bubblewrap

Bubblewrap is a low-level, unprivileged sandboxing tool for Linux that enables running applications in isolated environments with restricted access to the operating system and user data. This environment uses bubblewrap to execute commands in a sandboxed environment.

Warning

This environment is experimental.

Warning

This environment is not supported on Windows.

BubblewrapEnvironmentConfig

Bases: BaseModel

cwd class-attribute instance-attribute

cwd: str = ''

Working directory for the sandbox.

env class-attribute instance-attribute

env: dict[str, str] = {}

Dictionary of environment variables to set in the sandbox.

timeout class-attribute instance-attribute

timeout: int = 30

Timeout for the command in seconds.

executable class-attribute instance-attribute

executable: str = getenv(
    "MSWEA_BUBBLEWRAP_EXECUTABLE", "bwrap"
)

Path to the bubblewrap executable.

wrapper_args class-attribute instance-attribute

wrapper_args: list[str] = [
    "--unshare-user-try",
    "--ro-bind",
    "/usr",
    "/usr",
    "--ro-bind",
    "/bin",
    "/bin",
    "--ro-bind",
    "/lib",
    "/lib",
    "--ro-bind",
    "/lib64",
    "/lib64",
    "--ro-bind",
    "/etc",
    "/etc",
    "--tmpfs",
    "/tmp",
    "--proc",
    "/proc",
    "--dev",
    "/dev",
    "--new-session",
    "--setenv",
    "PATH",
    "/usr/local/bin:/usr/sbin:/usr/bin:/bin",
]

Arguments to pass to the bubblewrap executable.

BubblewrapEnvironment

BubblewrapEnvironment(
    *,
    config_class: type = BubblewrapEnvironmentConfig,
    logger: Logger | None = None,
    **kwargs,
)

This class executes bash commands in a bubblewrap environment and a separate working directory for each environment. See BubblewrapEnvironmentConfig for kwargs.

Source code in src/minisweagent/environments/extra/bubblewrap.py
70
71
72
73
74
75
76
77
78
79
def __init__(
    self, *, config_class: type = BubblewrapEnvironmentConfig, logger: logging.Logger | None = None, **kwargs
):
    """This class executes bash commands in a bubblewrap environment and a separate working
    directory for each environment. See `BubblewrapEnvironmentConfig` for kwargs.
    """
    self.logger = logger or logging.getLogger("minisweagent.environment")
    self.config = config_class(**kwargs)
    self.working_dir = Path(tempfile.gettempdir()) / f"minisweagent-{uuid.uuid4().hex[:8]}"
    self.working_dir.mkdir(parents=True)

logger instance-attribute

logger = logger or getLogger('minisweagent.environment')

config instance-attribute

config = config_class(**kwargs)

working_dir instance-attribute

working_dir = Path(gettempdir()) / f"minisweagent-{hex[:8]}"

execute

execute(
    action: dict,
    cwd: str = "",
    *,
    timeout: int | None = None,
) -> dict[str, Any]

Execute a command in the bubblewrap environment and return the result as a dict.

Source code in src/minisweagent/environments/extra/bubblewrap.py
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def execute(self, action: dict, cwd: str = "", *, timeout: int | None = None) -> dict[str, Any]:
    """Execute a command in the bubblewrap environment and return the result as a dict."""
    command = action.get("command", "")
    cwd = cwd or self.config.cwd or str(self.working_dir)

    cmd = [self.config.executable] + self.config.wrapper_args + ["--bind", cwd, cwd, "--chdir", cwd]

    # Add environment variables
    for key, value in self.config.env.items():
        cmd.extend(["--setenv", key, value])

    cmd.extend(["bash", "-c", command])

    try:
        result = subprocess.run(
            cmd,
            text=True,
            timeout=timeout or self.config.timeout,
            encoding="utf-8",
            errors="replace",
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
        )
        output = {"output": result.stdout, "returncode": result.returncode, "exception_info": ""}
    except Exception as e:
        raw_output = getattr(e, "output", None)
        raw_output = (
            raw_output.decode("utf-8", errors="replace") if isinstance(raw_output, bytes) else (raw_output or "")
        )
        output = {
            "output": raw_output,
            "returncode": -1,
            "exception_info": f"An error occurred while executing the command: {e}",
            "extra": {"exception_type": type(e).__name__, "exception": str(e)},
        }
    self._check_finished(output)
    return output

cleanup

cleanup()
Source code in src/minisweagent/environments/extra/bubblewrap.py
132
133
134
def cleanup(self):
    if self.working_dir.exists():
        shutil.rmtree(self.working_dir)

get_template_vars

get_template_vars(**kwargs) -> dict[str, Any]
Source code in src/minisweagent/environments/extra/bubblewrap.py
140
141
def get_template_vars(self, **kwargs) -> dict[str, Any]:
    return recursive_merge(self.config.model_dump(), platform.uname()._asdict(), kwargs)

serialize

serialize() -> dict
Source code in src/minisweagent/environments/extra/bubblewrap.py
143
144
145
146
147
148
149
150
151
def serialize(self) -> dict:
    return {
        "info": {
            "config": {
                "environment": self.config.model_dump(mode="json"),
                "environment_type": f"{self.__class__.__module__}.{self.__class__.__name__}",
            }
        }
    }