ci-automation: Properly escape parameters passed to bash

Forwarding parameters to another bash invocation through a string
interpreted as a bash script is a bit troublesome. It is not enough to
wrap a parameter like 'foo bar' in escaped double quotes (\") to avoid
it being split into two parameters by bash executing the script
string. It mostly works, but there's always a risk of having a path
where this breaks. It's rare

Wrapping into escaped quotes, be them double or single, also won't
work for passing an array of parameters, so it's even easier here to
trigger globbing or bracket expansion or another unwanted splitting of
supposedly one parameter into multiple. Globbing can be temporarily
disabled with 'set -f' or 'set -o noglob', but this still leaves all
the other special bash characters unescaped. So each parameter in the
array should be escaped before they are put into the script string.

The escaping can be done with `printf` and its '%q` formatter, so
let's do so. For single parameters it is as simple as
`foo_escaped=$(printf '%q' "${foo}")`, for arrays a loop needs to be
used.
This commit is contained in:
Krzesimir Nowak 2023-04-27 11:44:26 +02:00 committed by Kai Lueke
parent 34d488865a
commit 565b0b7784

View File

@ -118,6 +118,21 @@ function __prepare_torcx() {
}
# --
function __escape_multiple() {
local out_array_arg_name="${1}"; shift
# rest are args to be escape and appended into the array named
# after the first arg
local -n out_array_arg_ref="${out_array_arg_name}"
local arg arg_escaped
out_array_arg_ref=()
for arg; do
printf -v arg_escaped '%q' "${arg}"
out_array_arg_ref+=( "${arg_escaped}" )
done
}
# --
function test_run() {
# Run a subshell, so the traps, environment changes and global
# variables are not spilled into the caller.
@ -177,11 +192,27 @@ function _test_run_impl() {
# A job on each worker prunes old mantle images (docker image prune)
echo "docker rm -f '${container_name}'" >> ./ci-cleanup.sh
local image_escaped
printf -v image_escaped '%q' "${image}"
local common_test_args=(
"${work_dir}"
"${tests_dir}"
"${arch}"
"${vernum}"
)
local common_test_args_escaped=()
__escape_multiple common_test_args_escaped "${common_test_args[@]}"
local tests_escaped=()
__escape_multiple tests_escaped "${@}"
# Vendor tests may need to know if it is a first run or a rerun
touch "${work_dir}/first_run"
for retry in $(seq "${retries}"); do
local tapfile="results-run-${retry}.tap"
local failfile="failed-run-${retry}.txt"
local tapfile_escaped
printf -v tapfile_escaped '%q' "${tapfile}"
# Ignore retcode since tests are flaky. We'll re-run failed tests and
# determine success based on test results (tapfile).
@ -189,13 +220,9 @@ function _test_run_impl() {
touch sdk_container/.env
docker run --pull always --rm --name="${container_name}" --privileged --net host -v /dev:/dev \
-w /work -v "$PWD":/work "${mantle_ref}" \
bash -c "set -o noglob && git config --global --add safe.directory /work && source sdk_container/.env && ci-automation/vendor-testing/${image}.sh \
\"${work_dir}\" \
\"${tests_dir}\" \
\"${arch}\" \
\"${vernum}\" \
\"${tapfile}\" \
$*"
bash -c "git config --global --add safe.directory /work && \
source sdk_container/.env && \
ci-automation/vendor-testing/${image_escaped}.sh ${common_test_args_escaped[*]} ${tapfile_escaped} ${tests_escaped[*]}"
set -e
rm -f "${work_dir}/first_run"
@ -230,10 +257,9 @@ function _test_run_impl() {
echo "Failed tests:"
printf '%s\n' "${failed_tests[@]}"
echo "-----------"
set -- "${failed_tests[@]}"
__escape_multiple tests_escaped "${failed_tests[@]}"
done
if ${print_give_up}; then
echo "########### All re-runs exhausted ($retries). Giving up. ###########"
fi