human-eval-rust 2.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/HumanEval_rust.jsonl +164 -0
- data/HumanEval_rust_extended.jsonl +2 -0
- data/example_rust_problem.jsonl +1 -0
- data/example_rust_samples.jsonl +4 -0
- human_eval/__init__.py +23 -0
- human_eval/data.py +74 -0
- human_eval/evaluate_functional_correctness.py +112 -0
- human_eval/evaluation.py +281 -0
- human_eval/execution.py +186 -0
- human_eval/logging_config.py +43 -0
- human_eval/resource_monitor.py +58 -0
- human_eval/rust_execution.py +802 -0
- human_eval/sandbox.py +586 -0
- human_eval_rust-2.1.0.dist-info/METADATA +488 -0
- human_eval_rust-2.1.0.dist-info/RECORD +19 -0
- human_eval_rust-2.1.0.dist-info/WHEEL +5 -0
- human_eval_rust-2.1.0.dist-info/entry_points.txt +2 -0
- human_eval_rust-2.1.0.dist-info/licenses/LICENSE +21 -0
- human_eval_rust-2.1.0.dist-info/top_level.txt +1 -0
human_eval/sandbox.py
ADDED
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Sandbox wrapper for Rust code evaluation in HumanEval.
|
|
3
|
+
|
|
4
|
+
This module provides secure isolation for running rustc commands on LLM-generated code.
|
|
5
|
+
Uses Firejail for isolation with explicit user choice for installation or fallback modes.
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2025 Dave Tofflemire, SigilDERG Project
|
|
8
|
+
Version: 2.1.0
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
import shutil
|
|
13
|
+
import subprocess
|
|
14
|
+
import sys
|
|
15
|
+
from typing import NamedTuple
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SandboxError(Exception):
|
|
19
|
+
"""Raised when sandbox operations fail."""
|
|
20
|
+
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class FirejailStatus(NamedTuple):
|
|
25
|
+
"""Result of Firejail availability check."""
|
|
26
|
+
|
|
27
|
+
available: bool
|
|
28
|
+
version: str | None
|
|
29
|
+
error: str | None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class InstallResult(NamedTuple):
|
|
33
|
+
"""Result of Firejail installation attempt."""
|
|
34
|
+
|
|
35
|
+
success: bool
|
|
36
|
+
stdout: str
|
|
37
|
+
stderr: str
|
|
38
|
+
exit_code: int
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# Cache for rustc validation (avoid checking on every call)
|
|
42
|
+
_host_rustc_validated = False
|
|
43
|
+
_firejail_validated = False
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
FIREJAIL_SECURITY_OPTS = [
|
|
47
|
+
"--seccomp",
|
|
48
|
+
"--caps.drop=all",
|
|
49
|
+
"--noroot",
|
|
50
|
+
"--rlimit-fsize=104857600",
|
|
51
|
+
"--rlimit-nproc=50",
|
|
52
|
+
"--rlimit-cpu=120",
|
|
53
|
+
"--read-only=/",
|
|
54
|
+
"--private-tmp",
|
|
55
|
+
"--nogroups",
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def check_firejail_available() -> FirejailStatus:
|
|
60
|
+
"""
|
|
61
|
+
Check if Firejail is available and get version info.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
FirejailStatus with availability, version, and any error message.
|
|
65
|
+
"""
|
|
66
|
+
try:
|
|
67
|
+
result = subprocess.run(
|
|
68
|
+
["firejail", "--version"],
|
|
69
|
+
capture_output=True,
|
|
70
|
+
text=True,
|
|
71
|
+
timeout=5,
|
|
72
|
+
)
|
|
73
|
+
if result.returncode == 0:
|
|
74
|
+
# Extract version from output (first line typically contains version)
|
|
75
|
+
version_line = (
|
|
76
|
+
result.stdout.strip().split("\n")[0] if result.stdout else None
|
|
77
|
+
)
|
|
78
|
+
return FirejailStatus(available=True, version=version_line, error=None)
|
|
79
|
+
return FirejailStatus(
|
|
80
|
+
available=False,
|
|
81
|
+
version=None,
|
|
82
|
+
error=f"firejail returned exit code {result.returncode}: {result.stderr}",
|
|
83
|
+
)
|
|
84
|
+
except subprocess.TimeoutExpired:
|
|
85
|
+
return FirejailStatus(
|
|
86
|
+
available=False, version=None, error="firejail --version timed out"
|
|
87
|
+
)
|
|
88
|
+
except FileNotFoundError:
|
|
89
|
+
return FirejailStatus(
|
|
90
|
+
available=False, version=None, error="firejail not found in PATH"
|
|
91
|
+
)
|
|
92
|
+
except Exception as e:
|
|
93
|
+
return FirejailStatus(available=False, version=None, error=str(e))
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def detect_package_manager() -> str | None:
|
|
97
|
+
"""
|
|
98
|
+
Detect the available package manager for Firejail installation.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Package manager command prefix or None if not detected.
|
|
102
|
+
"""
|
|
103
|
+
# Check for common package managers in order of preference
|
|
104
|
+
package_managers = [
|
|
105
|
+
("apt-get", ["sudo", "apt-get", "install", "-y", "firejail"]),
|
|
106
|
+
("dnf", ["sudo", "dnf", "install", "-y", "firejail"]),
|
|
107
|
+
("yum", ["sudo", "yum", "install", "-y", "firejail"]),
|
|
108
|
+
("pacman", ["sudo", "pacman", "-S", "--noconfirm", "firejail"]),
|
|
109
|
+
("zypper", ["sudo", "zypper", "install", "-y", "firejail"]),
|
|
110
|
+
("apk", ["sudo", "apk", "add", "firejail"]),
|
|
111
|
+
]
|
|
112
|
+
|
|
113
|
+
for name, _ in package_managers:
|
|
114
|
+
if shutil.which(name):
|
|
115
|
+
return name
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def get_install_command() -> list[str] | None:
|
|
120
|
+
"""
|
|
121
|
+
Get the appropriate Firejail installation command for this system.
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
Installation command as list or None if no package manager found.
|
|
125
|
+
"""
|
|
126
|
+
pm = detect_package_manager()
|
|
127
|
+
if pm == "apt-get":
|
|
128
|
+
return ["sudo", "apt-get", "install", "-y", "firejail"]
|
|
129
|
+
elif pm == "dnf":
|
|
130
|
+
return ["sudo", "dnf", "install", "-y", "firejail"]
|
|
131
|
+
elif pm == "yum":
|
|
132
|
+
return ["sudo", "yum", "install", "-y", "firejail"]
|
|
133
|
+
elif pm == "pacman":
|
|
134
|
+
return ["sudo", "pacman", "-S", "--noconfirm", "firejail"]
|
|
135
|
+
elif pm == "zypper":
|
|
136
|
+
return ["sudo", "zypper", "install", "-y", "firejail"]
|
|
137
|
+
elif pm == "apk":
|
|
138
|
+
return ["sudo", "apk", "add", "firejail"]
|
|
139
|
+
return None
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def attempt_firejail_install() -> InstallResult:
|
|
143
|
+
"""
|
|
144
|
+
Attempt to install Firejail using the system package manager.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
InstallResult with success status, stdout, stderr, and exit code.
|
|
148
|
+
"""
|
|
149
|
+
install_cmd = get_install_command()
|
|
150
|
+
if install_cmd is None:
|
|
151
|
+
return InstallResult(
|
|
152
|
+
success=False,
|
|
153
|
+
stdout="",
|
|
154
|
+
stderr="No supported package manager found (apt-get, dnf, yum, pacman, zypper, apk)",
|
|
155
|
+
exit_code=-1,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
print(f"Attempting to install Firejail: {' '.join(install_cmd)}", file=sys.stderr)
|
|
159
|
+
try:
|
|
160
|
+
result = subprocess.run(
|
|
161
|
+
install_cmd,
|
|
162
|
+
capture_output=True,
|
|
163
|
+
text=True,
|
|
164
|
+
timeout=300, # 5 minutes max for installation
|
|
165
|
+
)
|
|
166
|
+
return InstallResult(
|
|
167
|
+
success=result.returncode == 0,
|
|
168
|
+
stdout=result.stdout,
|
|
169
|
+
stderr=result.stderr,
|
|
170
|
+
exit_code=result.returncode,
|
|
171
|
+
)
|
|
172
|
+
except subprocess.TimeoutExpired:
|
|
173
|
+
return InstallResult(
|
|
174
|
+
success=False,
|
|
175
|
+
stdout="",
|
|
176
|
+
stderr="Installation timed out after 5 minutes",
|
|
177
|
+
exit_code=-1,
|
|
178
|
+
)
|
|
179
|
+
except Exception as e:
|
|
180
|
+
return InstallResult(
|
|
181
|
+
success=False,
|
|
182
|
+
stdout="",
|
|
183
|
+
stderr=str(e),
|
|
184
|
+
exit_code=-1,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def prompt_sandbox_choice(firejail_error: str | None = None) -> str:
|
|
189
|
+
"""
|
|
190
|
+
Present interactive choice to user when Firejail is unavailable.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
firejail_error: Error message from Firejail check, if any.
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
Chosen mode: "firejail", "none", or raises SystemExit on cancel.
|
|
197
|
+
"""
|
|
198
|
+
print("\n" + "=" * 70, file=sys.stderr)
|
|
199
|
+
print("SANDBOX CONFIGURATION REQUIRED", file=sys.stderr)
|
|
200
|
+
print("=" * 70, file=sys.stderr)
|
|
201
|
+
|
|
202
|
+
if firejail_error:
|
|
203
|
+
print(f"\nFirejail is not available: {firejail_error}", file=sys.stderr)
|
|
204
|
+
else:
|
|
205
|
+
print("\nFirejail sandbox is not available on this system.", file=sys.stderr)
|
|
206
|
+
|
|
207
|
+
print("\nOptions:", file=sys.stderr)
|
|
208
|
+
print(" [1] Install Firejail (requires sudo, Linux only)", file=sys.stderr)
|
|
209
|
+
print(" [2] Cancel and exit", file=sys.stderr)
|
|
210
|
+
print(
|
|
211
|
+
" [3] Proceed WITHOUT sandboxing (UNSAFE - for trusted code only)",
|
|
212
|
+
file=sys.stderr,
|
|
213
|
+
)
|
|
214
|
+
print("\n" + "-" * 70, file=sys.stderr)
|
|
215
|
+
|
|
216
|
+
while True:
|
|
217
|
+
try:
|
|
218
|
+
choice = input("Enter choice [1/2/3]: ").strip()
|
|
219
|
+
except (EOFError, KeyboardInterrupt):
|
|
220
|
+
print("\nOperation cancelled by user.", file=sys.stderr)
|
|
221
|
+
raise SystemExit(1)
|
|
222
|
+
|
|
223
|
+
if choice == "1":
|
|
224
|
+
# Attempt installation
|
|
225
|
+
install_result = attempt_firejail_install()
|
|
226
|
+
if install_result.success:
|
|
227
|
+
# Verify installation worked
|
|
228
|
+
status = check_firejail_available()
|
|
229
|
+
if status.available:
|
|
230
|
+
print(
|
|
231
|
+
f"\n✓ Firejail installed successfully: {status.version}",
|
|
232
|
+
file=sys.stderr,
|
|
233
|
+
)
|
|
234
|
+
return "firejail"
|
|
235
|
+
else:
|
|
236
|
+
print(
|
|
237
|
+
f"\n✗ Firejail installation appeared to succeed but verification failed: {status.error}",
|
|
238
|
+
file=sys.stderr,
|
|
239
|
+
)
|
|
240
|
+
else:
|
|
241
|
+
print("\n" + "-" * 70, file=sys.stderr)
|
|
242
|
+
print("FIREJAIL INSTALLATION FAILED", file=sys.stderr)
|
|
243
|
+
print("-" * 70, file=sys.stderr)
|
|
244
|
+
print(f"Exit code: {install_result.exit_code}", file=sys.stderr)
|
|
245
|
+
if install_result.stderr:
|
|
246
|
+
# Show first 500 chars of stderr
|
|
247
|
+
stderr_preview = install_result.stderr[:500]
|
|
248
|
+
print(f"Error output:\n{stderr_preview}", file=sys.stderr)
|
|
249
|
+
print("-" * 70, file=sys.stderr)
|
|
250
|
+
print("\nInstallation failed. You may need to:", file=sys.stderr)
|
|
251
|
+
print(" - Run with elevated privileges", file=sys.stderr)
|
|
252
|
+
print(
|
|
253
|
+
" - Install Firejail manually from your distribution's package manager",
|
|
254
|
+
file=sys.stderr,
|
|
255
|
+
)
|
|
256
|
+
print(" - Visit: https://firejail.wordpress.com/", file=sys.stderr)
|
|
257
|
+
print("\nChoose another option:", file=sys.stderr)
|
|
258
|
+
print(" [2] Cancel and exit", file=sys.stderr)
|
|
259
|
+
print(" [3] Proceed WITHOUT sandboxing (UNSAFE)", file=sys.stderr)
|
|
260
|
+
|
|
261
|
+
elif choice == "2":
|
|
262
|
+
print("\nOperation cancelled by user.", file=sys.stderr)
|
|
263
|
+
raise SystemExit(1)
|
|
264
|
+
|
|
265
|
+
elif choice == "3":
|
|
266
|
+
print("\n" + "!" * 70, file=sys.stderr)
|
|
267
|
+
print("WARNING: PROCEEDING WITHOUT SANDBOX", file=sys.stderr)
|
|
268
|
+
print("!" * 70, file=sys.stderr)
|
|
269
|
+
print(
|
|
270
|
+
"\nYou are about to run untrusted LLM-generated code WITHOUT sandboxing.",
|
|
271
|
+
file=sys.stderr,
|
|
272
|
+
)
|
|
273
|
+
print(
|
|
274
|
+
"This is DANGEROUS and should only be done with trusted code.",
|
|
275
|
+
file=sys.stderr,
|
|
276
|
+
)
|
|
277
|
+
print("\nAre you sure you want to continue?", file=sys.stderr)
|
|
278
|
+
|
|
279
|
+
try:
|
|
280
|
+
confirm = input("Type 'yes' to confirm: ").strip().lower()
|
|
281
|
+
except (EOFError, KeyboardInterrupt):
|
|
282
|
+
print("\nOperation cancelled by user.", file=sys.stderr)
|
|
283
|
+
raise SystemExit(1)
|
|
284
|
+
|
|
285
|
+
if confirm == "yes":
|
|
286
|
+
print("\n⚠ Proceeding without sandboxing.", file=sys.stderr)
|
|
287
|
+
return "none"
|
|
288
|
+
else:
|
|
289
|
+
print(
|
|
290
|
+
"\nConfirmation not received. Please choose again:", file=sys.stderr
|
|
291
|
+
)
|
|
292
|
+
print(" [1] Install Firejail", file=sys.stderr)
|
|
293
|
+
print(" [2] Cancel and exit", file=sys.stderr)
|
|
294
|
+
print(" [3] Proceed WITHOUT sandboxing (UNSAFE)", file=sys.stderr)
|
|
295
|
+
|
|
296
|
+
else:
|
|
297
|
+
print("Invalid choice. Please enter 1, 2, or 3.", file=sys.stderr)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def resolve_sandbox_mode(
|
|
301
|
+
sandbox_mode: str | None,
|
|
302
|
+
allow_no_sandbox: bool = False,
|
|
303
|
+
non_interactive: bool = False,
|
|
304
|
+
) -> str:
|
|
305
|
+
"""
|
|
306
|
+
Resolve the sandbox mode based on availability and user preferences.
|
|
307
|
+
|
|
308
|
+
Args:
|
|
309
|
+
sandbox_mode: Requested mode ("firejail", "none", or None for auto-detect).
|
|
310
|
+
allow_no_sandbox: If True and non_interactive, allows unsandboxed mode without prompt.
|
|
311
|
+
non_interactive: If True, fails fast instead of prompting.
|
|
312
|
+
|
|
313
|
+
Returns:
|
|
314
|
+
Resolved sandbox mode ("firejail" or "none").
|
|
315
|
+
|
|
316
|
+
Raises:
|
|
317
|
+
SandboxError: If Firejail required but unavailable in non-interactive mode.
|
|
318
|
+
SystemExit: If user cancels interactive prompt.
|
|
319
|
+
"""
|
|
320
|
+
# Explicit firejail mode
|
|
321
|
+
if sandbox_mode == "firejail":
|
|
322
|
+
status = check_firejail_available()
|
|
323
|
+
if not status.available:
|
|
324
|
+
raise SandboxError(
|
|
325
|
+
f"Firejail sandbox mode requested but not available: {status.error}\n"
|
|
326
|
+
"Install Firejail or use --sandbox-mode=none (UNSAFE)."
|
|
327
|
+
)
|
|
328
|
+
return "firejail"
|
|
329
|
+
|
|
330
|
+
# Explicit none mode
|
|
331
|
+
if sandbox_mode == "none":
|
|
332
|
+
if not allow_no_sandbox and not non_interactive:
|
|
333
|
+
print(
|
|
334
|
+
"\n⚠ WARNING: Sandboxing disabled via --sandbox-mode=none",
|
|
335
|
+
file=sys.stderr,
|
|
336
|
+
)
|
|
337
|
+
print("This is UNSAFE for untrusted LLM-generated code!", file=sys.stderr)
|
|
338
|
+
return "none"
|
|
339
|
+
|
|
340
|
+
# Auto-detect mode (sandbox_mode is None or "auto")
|
|
341
|
+
status = check_firejail_available()
|
|
342
|
+
if status.available:
|
|
343
|
+
print(f"Using Firejail sandboxing ({status.version})", file=sys.stderr)
|
|
344
|
+
return "firejail"
|
|
345
|
+
|
|
346
|
+
# Firejail not available - handle based on mode
|
|
347
|
+
if non_interactive:
|
|
348
|
+
if allow_no_sandbox:
|
|
349
|
+
print(
|
|
350
|
+
f"\n⚠ WARNING: Firejail unavailable ({status.error}), proceeding unsandboxed",
|
|
351
|
+
file=sys.stderr,
|
|
352
|
+
)
|
|
353
|
+
return "none"
|
|
354
|
+
else:
|
|
355
|
+
raise SandboxError(
|
|
356
|
+
f"Firejail not available: {status.error}\n"
|
|
357
|
+
"Install Firejail, or use --allow-no-sandbox to proceed unsafely."
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
# Interactive mode - prompt user
|
|
361
|
+
return prompt_sandbox_choice(status.error)
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def run_rustc_with_firejail(
|
|
365
|
+
source_file: str,
|
|
366
|
+
output_binary: str,
|
|
367
|
+
command_args: list[str],
|
|
368
|
+
timeout: float = 30.0,
|
|
369
|
+
capture_output: bool = True,
|
|
370
|
+
) -> subprocess.CompletedProcess:
|
|
371
|
+
"""
|
|
372
|
+
Run rustc command using Firejail for sandboxing.
|
|
373
|
+
|
|
374
|
+
Args:
|
|
375
|
+
source_file: Path to the Rust source file
|
|
376
|
+
output_binary: Path to the output binary
|
|
377
|
+
command_args: Additional rustc arguments
|
|
378
|
+
timeout: Timeout in seconds
|
|
379
|
+
capture_output: Whether to capture stdout/stderr
|
|
380
|
+
|
|
381
|
+
Returns:
|
|
382
|
+
CompletedProcess with returncode, stdout, stderr
|
|
383
|
+
"""
|
|
384
|
+
source_dir = os.path.dirname(os.path.abspath(source_file))
|
|
385
|
+
|
|
386
|
+
rustc_cmd = (
|
|
387
|
+
[
|
|
388
|
+
"rustc",
|
|
389
|
+
]
|
|
390
|
+
+ command_args
|
|
391
|
+
+ [
|
|
392
|
+
os.path.basename(source_file),
|
|
393
|
+
"-o",
|
|
394
|
+
os.path.basename(output_binary),
|
|
395
|
+
]
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
# Firejail command with restrictions
|
|
399
|
+
# Memory limit: 4GB (same as previous Docker config for H100 optimization)
|
|
400
|
+
firejail_cmd = (
|
|
401
|
+
[
|
|
402
|
+
"firejail",
|
|
403
|
+
"--quiet",
|
|
404
|
+
"--net=none", # No network
|
|
405
|
+
"--private", # Private filesystem
|
|
406
|
+
"--private-cwd", # Private working directory
|
|
407
|
+
"--rlimit-as=4294967296", # 4GB memory limit (matches Docker config)
|
|
408
|
+
f"--timeout={int(timeout)}", # Timeout
|
|
409
|
+
"--cwd",
|
|
410
|
+
source_dir,
|
|
411
|
+
]
|
|
412
|
+
+ FIREJAIL_SECURITY_OPTS
|
|
413
|
+
+ rustc_cmd
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
try:
|
|
417
|
+
result = subprocess.run(
|
|
418
|
+
firejail_cmd,
|
|
419
|
+
cwd=source_dir,
|
|
420
|
+
capture_output=capture_output,
|
|
421
|
+
text=True,
|
|
422
|
+
timeout=timeout,
|
|
423
|
+
)
|
|
424
|
+
return result
|
|
425
|
+
except subprocess.TimeoutExpired:
|
|
426
|
+
return subprocess.CompletedProcess(
|
|
427
|
+
firejail_cmd, returncode=124, stdout="", stderr="Command timed out"
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def run_binary_with_firejail(
|
|
432
|
+
binary_path: str,
|
|
433
|
+
timeout: float = 30.0,
|
|
434
|
+
capture_output: bool = True,
|
|
435
|
+
) -> subprocess.CompletedProcess:
|
|
436
|
+
"""
|
|
437
|
+
Run a compiled binary using Firejail for sandboxing.
|
|
438
|
+
|
|
439
|
+
Args:
|
|
440
|
+
binary_path: Path to the binary
|
|
441
|
+
timeout: Timeout in seconds
|
|
442
|
+
capture_output: Whether to capture stdout/stderr
|
|
443
|
+
|
|
444
|
+
Returns:
|
|
445
|
+
CompletedProcess with returncode, stdout, stderr
|
|
446
|
+
"""
|
|
447
|
+
binary_dir = os.path.dirname(os.path.abspath(binary_path))
|
|
448
|
+
binary_name = os.path.basename(binary_path)
|
|
449
|
+
|
|
450
|
+
# Memory limit: 4GB (same as previous Docker config for H100 optimization)
|
|
451
|
+
firejail_cmd = (
|
|
452
|
+
[
|
|
453
|
+
"firejail",
|
|
454
|
+
"--quiet",
|
|
455
|
+
"--net=none",
|
|
456
|
+
"--private",
|
|
457
|
+
"--private-cwd",
|
|
458
|
+
"--rlimit-as=4294967296", # 4GB memory limit (matches Docker config)
|
|
459
|
+
f"--timeout={int(timeout)}",
|
|
460
|
+
"--cwd",
|
|
461
|
+
binary_dir,
|
|
462
|
+
]
|
|
463
|
+
+ FIREJAIL_SECURITY_OPTS
|
|
464
|
+
+ [f"./{binary_name}"]
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
try:
|
|
468
|
+
result = subprocess.run(
|
|
469
|
+
firejail_cmd,
|
|
470
|
+
cwd=binary_dir,
|
|
471
|
+
capture_output=capture_output,
|
|
472
|
+
text=True,
|
|
473
|
+
timeout=timeout,
|
|
474
|
+
)
|
|
475
|
+
return result
|
|
476
|
+
except subprocess.TimeoutExpired:
|
|
477
|
+
return subprocess.CompletedProcess(
|
|
478
|
+
firejail_cmd, returncode=124, stdout="", stderr="Command timed out"
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
def run_rustc_sandboxed(
|
|
483
|
+
source_file: str,
|
|
484
|
+
output_binary: str,
|
|
485
|
+
command_args: list[str],
|
|
486
|
+
timeout: float = 30.0,
|
|
487
|
+
capture_output: bool = True,
|
|
488
|
+
sandbox_mode: str | None = None,
|
|
489
|
+
) -> subprocess.CompletedProcess:
|
|
490
|
+
"""
|
|
491
|
+
Run rustc command with sandboxing (Firejail or none).
|
|
492
|
+
|
|
493
|
+
Args:
|
|
494
|
+
source_file: Path to the Rust source file
|
|
495
|
+
output_binary: Path to the output binary
|
|
496
|
+
command_args: Additional rustc arguments
|
|
497
|
+
timeout: Timeout in seconds
|
|
498
|
+
capture_output: Whether to capture stdout/stderr
|
|
499
|
+
sandbox_mode: "firejail", "none", or None (auto-detect)
|
|
500
|
+
|
|
501
|
+
Returns:
|
|
502
|
+
CompletedProcess with returncode, stdout, stderr
|
|
503
|
+
|
|
504
|
+
Raises:
|
|
505
|
+
SandboxError: If sandboxing is required but unavailable
|
|
506
|
+
"""
|
|
507
|
+
# Resolve sandbox mode if not already resolved
|
|
508
|
+
if sandbox_mode is None:
|
|
509
|
+
status = check_firejail_available()
|
|
510
|
+
if status.available:
|
|
511
|
+
sandbox_mode = "firejail"
|
|
512
|
+
else:
|
|
513
|
+
sandbox_mode = "none"
|
|
514
|
+
print(
|
|
515
|
+
f"[WARNING] Firejail not available ({status.error}). "
|
|
516
|
+
"Untrusted completions will run UNSANDBOXED.",
|
|
517
|
+
file=sys.stderr,
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
if sandbox_mode == "firejail":
|
|
521
|
+
return run_rustc_with_firejail(
|
|
522
|
+
source_file, output_binary, command_args, timeout, capture_output
|
|
523
|
+
)
|
|
524
|
+
elif sandbox_mode == "none":
|
|
525
|
+
# No sandboxing - only for local development with trusted code
|
|
526
|
+
# Validate rustc is available on host (fail fast if missing)
|
|
527
|
+
global _host_rustc_validated
|
|
528
|
+
if not _host_rustc_validated:
|
|
529
|
+
if shutil.which("rustc") is None:
|
|
530
|
+
raise SandboxError(
|
|
531
|
+
"rustc not found in PATH. "
|
|
532
|
+
"Install Rust toolchain or use sandbox_mode='firejail'."
|
|
533
|
+
)
|
|
534
|
+
_host_rustc_validated = True
|
|
535
|
+
|
|
536
|
+
rustc_cmd = ["rustc"] + command_args + [source_file, "-o", output_binary]
|
|
537
|
+
return subprocess.run(
|
|
538
|
+
rustc_cmd, capture_output=capture_output, text=True, timeout=timeout
|
|
539
|
+
)
|
|
540
|
+
else:
|
|
541
|
+
raise SandboxError(f"Unknown sandbox mode: {sandbox_mode}")
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
def run_binary_sandboxed(
|
|
545
|
+
binary_path: str,
|
|
546
|
+
timeout: float = 30.0,
|
|
547
|
+
capture_output: bool = True,
|
|
548
|
+
sandbox_mode: str | None = None,
|
|
549
|
+
) -> subprocess.CompletedProcess:
|
|
550
|
+
"""
|
|
551
|
+
Run a compiled binary with sandboxing (Firejail or none).
|
|
552
|
+
|
|
553
|
+
Args:
|
|
554
|
+
binary_path: Path to the binary
|
|
555
|
+
timeout: Timeout in seconds
|
|
556
|
+
capture_output: Whether to capture stdout/stderr
|
|
557
|
+
sandbox_mode: "firejail", "none", or None (auto-detect)
|
|
558
|
+
|
|
559
|
+
Returns:
|
|
560
|
+
CompletedProcess with returncode, stdout, stderr
|
|
561
|
+
|
|
562
|
+
Raises:
|
|
563
|
+
SandboxError: If sandboxing is required but unavailable
|
|
564
|
+
"""
|
|
565
|
+
# Resolve sandbox mode if not already resolved
|
|
566
|
+
if sandbox_mode is None:
|
|
567
|
+
status = check_firejail_available()
|
|
568
|
+
if status.available:
|
|
569
|
+
sandbox_mode = "firejail"
|
|
570
|
+
else:
|
|
571
|
+
sandbox_mode = "none"
|
|
572
|
+
print(
|
|
573
|
+
f"[WARNING] Firejail not available ({status.error}). "
|
|
574
|
+
"Untrusted completions will run UNSANDBOXED.",
|
|
575
|
+
file=sys.stderr,
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
if sandbox_mode == "firejail":
|
|
579
|
+
return run_binary_with_firejail(binary_path, timeout, capture_output)
|
|
580
|
+
elif sandbox_mode == "none":
|
|
581
|
+
# No sandboxing - only for local development with trusted code
|
|
582
|
+
return subprocess.run(
|
|
583
|
+
[binary_path], capture_output=capture_output, text=True, timeout=timeout
|
|
584
|
+
)
|
|
585
|
+
else:
|
|
586
|
+
raise SandboxError(f"Unknown sandbox mode: {sandbox_mode}")
|