From 7096183aee068ee59bd8163fe2c4a7a4f1483761 Mon Sep 17 00:00:00 2001 From: Hilary James Oliver Date: Fri, 25 Mar 2022 16:29:03 +1300 Subject: [PATCH 1/3] Escape quotes in remote command args. --- cylc/flow/scheduler_cli.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cylc/flow/scheduler_cli.py b/cylc/flow/scheduler_cli.py index e16ccc58624..09b405aaa21 100644 --- a/cylc/flow/scheduler_cli.py +++ b/cylc/flow/scheduler_cli.py @@ -18,6 +18,7 @@ from ansimarkup import parse as cparse import asyncio from functools import lru_cache +from shlex import quote import sys from typing import TYPE_CHECKING @@ -365,8 +366,9 @@ def _distribute(host): if not host: host = select_workflow_host()[0] if is_remote_host(host): + # quote here for (e.g.) --set='TEST="test"' + cmd = [quote(c) for c in sys.argv[1:]] # Prevent recursive host selection - cmd = sys.argv[1:] cmd.append("--host=localhost") _remote_cylc_cmd(cmd, host=host) sys.exit(0) From 489f4b93c1c2537fa2b2411009a9b1ac8a7eb1e1 Mon Sep 17 00:00:00 2001 From: Hilary James Oliver Date: Fri, 25 Mar 2022 16:33:02 +1300 Subject: [PATCH 2/3] Update change log. --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index e787506da9c..af96b18106f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -43,6 +43,10 @@ provided [wrapper script](https://cylc.github.io/cylc-doc/latest/html/installati ### Fixes +[#4769](https://github.com/cylc/cylc-flow/pull/4769) - Fix handling of quoted +command args for invocation on remote run hosts. + + [#4703](https://github.com/cylc/cylc-flow/pull/4703) - Fix `ImportError` when validating/running a Jinja2 workflow (for users who have installed Cylc using `pip`.) From 33d2670a3dd1e5f8be50f777939cb9418c8187df Mon Sep 17 00:00:00 2001 From: Hilary James Oliver Date: Mon, 28 Mar 2022 14:29:36 +1300 Subject: [PATCH 3/3] Add unit test. --- cylc/flow/scheduler_cli.py | 14 +++++++++++--- tests/unit/test_scheduler_cli.py | 24 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 tests/unit/test_scheduler_cli.py diff --git a/cylc/flow/scheduler_cli.py b/cylc/flow/scheduler_cli.py index 09b405aaa21..2ab793f18e7 100644 --- a/cylc/flow/scheduler_cli.py +++ b/cylc/flow/scheduler_cli.py @@ -20,7 +20,7 @@ from functools import lru_cache from shlex import quote import sys -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, List from cylc.flow import LOG, RSYNC_LOG from cylc.flow.exceptions import ServiceFileError @@ -360,14 +360,22 @@ def scheduler_cli(options: 'Values', workflow_id: str) -> None: sys.exit(ret) +def _protect_remote_cmd_args(cmd: List[str]) -> List[str]: + """Protect command args from second shell interpretation. + + Escape quoting for --set="FOO='foo'" args. + (Separate function for unit testing.) + """ + return [quote(c) for c in cmd] + + def _distribute(host): """Re-invoke this command on a different host if requested.""" # Check whether a run host is explicitly specified, else select one. if not host: host = select_workflow_host()[0] if is_remote_host(host): - # quote here for (e.g.) --set='TEST="test"' - cmd = [quote(c) for c in sys.argv[1:]] + cmd = _protect_remote_cmd_args(sys.argv[1:]) # Prevent recursive host selection cmd.append("--host=localhost") _remote_cylc_cmd(cmd, host=host) diff --git a/tests/unit/test_scheduler_cli.py b/tests/unit/test_scheduler_cli.py new file mode 100644 index 00000000000..f4b9cab2f63 --- /dev/null +++ b/tests/unit/test_scheduler_cli.py @@ -0,0 +1,24 @@ +# THIS FILE IS PART OF THE CYLC WORKFLOW ENGINE. +# Copyright (C) NIWA & British Crown (Met Office) & Contributors. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""Tests for Cylc scheduler CLI.""" + +from cylc.flow.scheduler_cli import _protect_remote_cmd_args + + +def test__protect_remote_cmd_args(): + cmd = ['cylc', 'play', '-n', '--set=FOO="foo"', 'wf'] + exp = ['cylc', 'play', '-n', '\'--set=FOO="foo"\'', 'wf'] + assert _protect_remote_cmd_args(cmd) == exp