Skip to content

Commit 0e7d847

Browse files
author
Nils Bars
committed
Add test_result_will_be_submitted and get_user_environment function
1 parent 84f7e40 commit 0e7d847

3 files changed

Lines changed: 49 additions & 34 deletions

File tree

ref_utils/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
"""Import functions to avoid .dot import for user""" # pylint: disable = invalid-name
2-
__all__ = ['process', 'assertion', 'checks', 'utils']
2+
__all__ = ['process', 'assertion', 'utils', "decorator"]
33
from .process import drop_privileges, run, get_payload_from_executable, ref_util_install_global_exception_hook, run_with_payload, run_capture_output
44
from .assertion import assert_is_dir, assert_is_exec, assert_is_file
5-
from .utils import print_ok, print_warn, print_err, write_stdout, decode_or_str
5+
from .utils import print_ok, print_warn, print_err, write_stdout, decode_or_str, test_result_will_be_submitted, get_user_environment
66
from .decorator import add_environment_test, add_submission_test, environment_test, submission_test, run_tests, TestResult
77

88
ref_util_install_global_exception_hook()

ref_utils/process.py

Lines changed: 7 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import importlib
1515
from functools import partial
1616

17-
from .utils import print_err, map_path_as_posix, print_ok, decode_or_str, print_warn
17+
from .utils import get_user_environment, print_err, map_path_as_posix, print_ok, decode_or_str, print_warn
1818
from .error import RefUtilsError, RefUtilsProcessTimeoutError, RefUtilsProcessError
1919

2020
_DEFAULT_DROP_UID = 9999
@@ -47,35 +47,6 @@ def ref_util_install_global_exception_hook() -> None:
4747
hook = partial(ref_util_exception_hook, redact_traceback=False)
4848
sys.excepthook = hook
4949

50-
51-
def get_user_env(last_cmd: Optional[Union[str, bytes]]) -> Dict[str, Union[str, bytes]]:
52-
"""
53-
The task tool (task-wrapper.c) dumps the user environment in the moment it is executed
54-
to disk. This function retrives the dumped environment from the file and returns it.
55-
This allows to restore the user's exact environment which is paramount for tasks that
56-
require a stable stack layout.
57-
Returns:
58-
The mapping of all key value pairs of the user environment variables that where
59-
defined during submission.
60-
"""
61-
ret: Dict[str, Union[str, bytes]] = {}
62-
content = Path('/tmp/.user_environ').read_text()
63-
lines = content.split('\x00')
64-
for line in lines:
65-
if line == '':
66-
continue
67-
68-
try:
69-
k, v = line.split('=', 1)
70-
except Exception as e:
71-
print_err(f'Unexpected error while processing "{line}". Error: {e}.')
72-
else:
73-
ret[k] = v
74-
75-
if last_cmd is not None:
76-
ret['_'] = last_cmd
77-
return ret
78-
7950
# Hopefully safe, if not, please tell us, dont mess with the system. Thanks :)
8051
class RestrictedUnpickler(pickle.Unpickler):
8152
ALLOWED_MODULE_NAME = {
@@ -169,7 +140,10 @@ def run(cmd_: List[Union[str, Path, bytes]], *args: str, **kwargs: Any) -> 'subp
169140
# Restore the environment from the user as of the time she called `task ...`.
170141
# NOTE: The stored environment contains user controlled input!
171142
# Never restore the environment in a privileged context.
172-
kwargs['env'] = get_user_env(cmd[0])
143+
env = get_user_environment()
144+
# Set the last executed command variable ("_") to the correct value.
145+
env["_"] = cmd[0]
146+
kwargs['env'] = env
173147

174148
if 'timeout' not in kwargs:
175149
kwargs['timeout'] = 10
@@ -206,13 +180,15 @@ def run_capture_output(*args: str, check_signal: bool = True, **kwargs: Any) ->
206180
Wrapper of subprocess.run that redirects stderr to stdout and returns
207181
(returncode, stdout). This methods raises the same exceptions as ref-utils
208182
run() method.
183+
If `env` is not set, the user environment when she called `task check` is restored.
209184
"""
210185
p = run(*args, **kwargs, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check_signal=check_signal)
211186
return p.returncode, p.stdout
212187

213188
def get_payload_from_executable(cmd_: List[Union[str, Path, bytes]], check: bool = True, check_signal: bool = True, verbose: bool = True, timeout: int = 10) -> Tuple[int, bytes]:
214189
"""
215190
Get the payload from a script/binary by executing it and returning the output.
191+
If `env` is not set, the user environment when she called `task check` is restored.
216192
Args:
217193
cmd: The command to execute.
218194
check: Same as for subprocess.run.

ref_utils/utils.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
""" Utility functions including colored printing or subprocess.run wrapper dropping privileges"""
22
import sys
3+
import os
4+
import typing as t
35
from typing import Any, AnyStr, List, Union
46

57
from pathlib import Path
@@ -24,18 +26,55 @@ def print_err(*args: str, **kwargs: Any) -> None:
2426
print(Fore.RED + _sep.join([str(o) for o in args]) + Style.RESET_ALL, **kwargs)
2527

2628

29+
def test_result_will_be_submitted() -> bool:
30+
"""
31+
Whether this test execution is going to be submitted as solution.
32+
"""
33+
val = os.environ.get("RESULT_WILL_BE_SUBMITTED")
34+
if val:
35+
return val.lower() in ["1", "true"]
36+
return False
37+
38+
def get_user_environment() -> t.Dict[str, Union[str, bytes]]:
39+
"""
40+
The task tool (task-wrapper.c) dumps the user environment in the moment it is executed
41+
to disk. This function retrives the dumped environment from the file and returns it.
42+
This allows to restore the user's exact environment which is paramount for tasks that
43+
require a stable stack layout.
44+
Returns:
45+
The mapping of all key value pairs of the user environment variables that where
46+
defined during submission.
47+
"""
48+
ret: t.Dict[str, Union[str, bytes]] = {}
49+
content = Path('/tmp/.user_environ').read_text()
50+
lines = content.split('\x00')
51+
for line in lines:
52+
if line == '':
53+
continue
54+
55+
try:
56+
k, v = line.split('=', 1)
57+
except Exception as e:
58+
print_err(f'Unexpected error while processing "{line}". Error: {e}.')
59+
else:
60+
ret[k] = v
61+
return ret
62+
2763
def write_stdout(data: AnyStr) -> None:
2864
sys.stdout.write(data) # type: ignore
2965

3066

31-
def decode_or_str(data: bytes) -> str:
67+
def decode_or_str(data: str | bytes | bytearray) -> str:
3268
"""
3369
Get a str representing the passed `data`.
3470
If the bytes are valid UTF8 they are converted to a str.
3571
Else, they are converted to str via `str(data)`.
3672
"""
3773
if not data:
3874
return ''
75+
if isinstance(data, str):
76+
return data
77+
3978
try:
4079
return data.decode()
4180
except: # pylint: disable =bare-except

0 commit comments

Comments
 (0)