From d5ce90cc92bc274a1906bbc99a33d5ce161878d1 Mon Sep 17 00:00:00 2001 From: "randomizedcoder dave.seddon.ca@gmail.com" Date: Fri, 18 Sep 2026 10:47:59 -0700 Subject: [PATCH] fix(cloudlab): quote ssh arguments in on_nodes (SC2068) on_nodes forwarded the remote command with a bare, unquoted $@: ssh -4 $node $@ An unquoted $@ undergoes word splitting and glob expansion in the caller before ssh sees it, so any argument that contains spaces is split into several arguments, an argument containing a glob metacharacter is expanded against the local filesystem, and an empty-string argument is dropped entirely. shellcheck reports this as SC2068 (error). Quote both the node and the forwarded arguments so word boundaries reach ssh intact: ssh -4 "$node" "$@" Also add the missing newline at end of file. Gate: `shellcheck --include=SC2068 cloudlab/bin/on_nodes` reports the SC2068 error before and is clean after. A bats scaffold (cloudlab/tests/on_nodes.bats) stubs ssh on PATH and asserts argument boundaries survive: 3 of its cases (spaces / glob char / empty arg) fail against the old bare-$@ script and all pass after the fix; no new CI is wired. The pre-existing SC2004 style note on the arithmetic loop is left untouched as out of scope. Co-Authored-By: Claude Opus 4.8 --- cloudlab/bin/on_nodes | 4 +- cloudlab/tests/on_nodes.bats | 80 ++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 cloudlab/tests/on_nodes.bats diff --git a/cloudlab/bin/on_nodes b/cloudlab/bin/on_nodes index a13763d9..26c8993e 100755 --- a/cloudlab/bin/on_nodes +++ b/cloudlab/bin/on_nodes @@ -25,5 +25,5 @@ for ((i = $first ; i <= $last; i++)); do node=node$i echo "" echo $node: - ssh -4 $node $@ -done \ No newline at end of file + ssh -4 "$node" "$@" +done diff --git a/cloudlab/tests/on_nodes.bats b/cloudlab/tests/on_nodes.bats new file mode 100644 index 00000000..a4f08834 --- /dev/null +++ b/cloudlab/tests/on_nodes.bats @@ -0,0 +1,80 @@ +#!/usr/bin/env bats + +# SPDX-License-Identifier: BSD-1-Clause +# +# Regression tests for cloudlab/bin/on_nodes. +# +# These document the SC2068 fix: the remote command and its arguments +# must be forwarded to ssh with their word boundaries intact, which +# requires "$@" (quoted) rather than a bare $@. With an unquoted $@ an +# argument containing spaces or glob characters is re-split / expanded by +# the loop's word splitting before ssh ever sees it. +# +# Run: nix shell nixpkgs#bats -c bats cloudlab/tests/on_nodes.bats +# +# No new CI is wired; this file is included as executable documentation. + +setup() { + ON_NODES="${BATS_TEST_DIRNAME}/../bin/on_nodes" + + # Stub PATH with a fake ssh that records exactly the argv it receives, + # one argument per line, so tests can assert on argument boundaries. + STUB_DIR="$(mktemp -d)" + ARGV_LOG="${STUB_DIR}/argv.log" + cat > "${STUB_DIR}/ssh" < "${ARGV_LOG}" +for a in "\$@"; do + printf '%s\n' "\$a" >> "${ARGV_LOG}" +done +EOF + chmod +x "${STUB_DIR}/ssh" + PATH="${STUB_DIR}:${PATH}" +} + +teardown() { + rm -rf "${STUB_DIR}" +} + +# Argument count that ssh saw, minus the leading `-4 nodeN` (2 args). +forwarded_count() { + echo $(( $(wc -l < "${ARGV_LOG}") - 2 )) +} + +@test "usage error when fewer than 3 args" { + run "$ON_NODES" 1 + [ "$status" -eq 1 ] + [[ "$output" == Usage:* ]] +} + +@test "simple command is forwarded verbatim" { + run "$ON_NODES" 1 1 echo hello + [ "$status" -eq 0 ] + # ssh argv: -4 node1 echo hello + run cat "${ARGV_LOG}" + [ "${lines[0]}" = "-4" ] + [ "${lines[1]}" = "node1" ] + [ "${lines[2]}" = "echo" ] + [ "${lines[3]}" = "hello" ] +} + +@test "argument containing spaces stays a single argument" { + "$ON_NODES" 1 1 echo "one two three" + # 2 forwarded args: 'echo' and 'one two three' (NOT split into 4). + [ "$(forwarded_count)" -eq 2 ] + run cat "${ARGV_LOG}" + [ "${lines[3]}" = "one two three" ] +} + +@test "argument with a glob char is not expanded by on_nodes" { + "$ON_NODES" 1 1 ls '*.c' + [ "$(forwarded_count)" -eq 2 ] + run cat "${ARGV_LOG}" + [ "${lines[3]}" = "*.c" ] +} + +@test "empty-string argument is preserved as one argument" { + "$ON_NODES" 1 1 echo "" + # 'echo' + one empty arg = 2 forwarded args. + [ "$(forwarded_count)" -eq 2 ] +}