Skip to content

Commit

Permalink
Better escape command line arguments
Browse files Browse the repository at this point in the history
Instead of using `shlex.quote` and then manually joining commands, use
`shlex.join`, which has better results, especially for flags and
other options.
  • Loading branch information
niknetniko committed Dec 6, 2023
1 parent 1a0a90f commit b323fc5
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 14 deletions.
2 changes: 1 addition & 1 deletion tested/languages/bash/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ def convert_execution_unit(pu: PreparedExecutionUnit) -> str:
result += indent + "write_separator\n"
assert isinstance(tc.input, MainInput)
result += f"{indent}bash {submission_file(pu.language)} "
result += " ".join(shlex.quote(s) for s in tc.input.arguments) + "\n"
result += shlex.join(tc.input.arguments) + "\n"
else:
if j != 0:
result += indent + "write_separator\n"
Expand Down
13 changes: 2 additions & 11 deletions tested/languages/generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,6 @@ def _handle_link_files(link_files: Iterable[FileUrl], language: str) -> tuple[st
)


def _escape_shell(arg: str) -> str:
# We want to always quote...
quoted = shlex.quote(arg)
if quoted.startswith("'") and quoted.endswith("'"):
return quoted
else:
return f"'{quoted}'"


def get_readable_input(
bundle: Bundle, case: Testcase
) -> tuple[ExtendedMessage, set[FileUrl]]:
Expand All @@ -122,9 +113,9 @@ def get_readable_input(
assert isinstance(case.input, MainInput)
# See https://rouge-ruby.github.io/docs/Rouge/Lexers/ConsoleLexer.html
format_ = "console"
arguments = " ".join(_escape_shell(x) for x in case.input.arguments)
submission = submission_name(bundle.language)
args = f"$ {submission} {arguments}"
command = shlex.join([submission] + case.input.arguments)
args = f"$ {command}"
if isinstance(case.input.stdin, TextData):
stdin = case.input.stdin.get_data_as_string(bundle.config.resources)
else:
Expand Down
25 changes: 23 additions & 2 deletions tests/test_functionality.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@
from tested.judge.execution import ExecutionResult
from tested.languages import LANGUAGES
from tested.languages.conventionalize import submission_name
from tested.languages.generation import generate_statement
from tested.languages.generation import generate_statement, get_readable_input
from tested.serialisation import (
BooleanType,
FunctionCall,
FunctionType,
NumberType,
StringType,
)
from tested.testsuite import Suite
from tested.testsuite import Context, MainInput, Suite, Tab, Testcase
from tests.manual_utils import assert_valid_output, configuration, execute_config

COMPILE_LANGUAGES = [
Expand Down Expand Up @@ -1015,3 +1015,24 @@ def test_output_in_script_is_caught(language: str, tmp_path: Path, pytestconfig)
print(result)
updates = assert_valid_output(result, pytestconfig)
assert updates.find_status_enum() == ["wrong", "correct", "correct"]


def test_main_call_quotes(tmp_path: Path, pytestconfig):
conf = configuration(
pytestconfig,
"echo-function",
"bash",
tmp_path,
"two.yaml",
"top-level-output",
)
the_input = Testcase(
input=MainInput(arguments=["hello", "it's", "$yes", "--hello=no", "-hello"])
)
suite = Suite(tabs=[Tab(contexts=[Context(testcases=[the_input])], name="hallo")])
bundle = create_bundle(conf, sys.stdout, suite)
actual, _ = get_readable_input(bundle, the_input)

assert (
actual.description == "$ submission hello 'it'\"'\"'s' '$yes' --hello=no -hello"
)

0 comments on commit b323fc5

Please sign in to comment.