Skip to content

Commit 3b7a54f

Browse files
afilinafredden
andauthored
Enhance command replacer (#38)
- Use functions for easier reasoning about the code. - Compare commands to an allow list; easier to read and scalable. - Check for unsafe tokens. - Add default options so we don't have to repeat them in every COMMAND-OUTPUT. - Replace eval with ${EXECUTABLE_COMMAND[@]} to prevent injections. Example errors: `ls -l` ERROR: refusing to run arbitrary command: ls `phpcs && ls -l` ERROR: refusing unsafe token: && Co-authored-by: Dan Wallis <[email protected]>
1 parent fd371e0 commit 3b7a54f

File tree

1 file changed

+37
-13
lines changed

1 file changed

+37
-13
lines changed

build/wiki-command-replacer.sh

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,40 @@ cd "$(dirname "$0")/.."
66

77
MARKER_START='{{COMMAND-OUTPUT "'
88
MARKER_END='"}}'
9+
ALLOWED_COMMANDS=("phpcs" "phpcbf")
10+
11+
tokenize_command() {
12+
read -ra TOKENS <<< "$1"
13+
}
14+
15+
check_allowed_commands() {
16+
local cmd="${TOKENS[0]}"
17+
for allowed in "${ALLOWED_COMMANDS[@]}"; do
18+
[[ "${cmd}" == "${allowed}" ]] && return 0
19+
done
20+
21+
echo >&2 " ERROR: refusing to run arbitrary command: ${cmd}"
22+
exit 1
23+
}
24+
25+
validate_tokens() {
26+
for token in "${TOKENS[@]}"; do
27+
if [[ "${token}" =~ [\;\|\&\$\<\>\`\\] ]]; then
28+
echo >&2 " ERROR: refusing unsafe token: ${token}"
29+
exit 1
30+
fi
31+
done
32+
}
33+
34+
execute_command() {
35+
tokenize_command "$1"
36+
check_allowed_commands
37+
validate_tokens
38+
39+
EXECUTABLE_COMMAND=("${TOKENS[0]}" "${TOKENS[@]:1}")
40+
echo >&2 " INFO: running: " "${EXECUTABLE_COMMAND[@]}"
41+
"${EXECUTABLE_COMMAND[@]}" </dev/null || true
42+
}
943

1044
if [[ -z "${CI:-}" ]]; then
1145
# The `_wiki` directory is created in a previous GitHub Action step.
@@ -19,20 +53,10 @@ grep -lrF "${MARKER_START}" _wiki | while read -r file_to_process; do
1953

2054
while IFS=$'\n' read -r line; do
2155
if [[ ${line} = ${MARKER_START}*${MARKER_END} ]]; then
22-
COMMAND="${line##"${MARKER_START}"}"
23-
COMMAND="${COMMAND%%"${MARKER_END}"}"
24-
25-
if [[ "${COMMAND}" != "phpcs "* ]] && [[ "${COMMAND}" != "phpcbf "* ]]; then
26-
echo >&2 " ERROR: refusing to run arbitrary command: ${COMMAND}"
27-
exit 1
28-
fi
29-
30-
#FIXME refuse to run commands with a semicolon / pipe / ampersand / sub-shell
56+
USER_COMMAND="${line##"${MARKER_START}"}"
57+
USER_COMMAND="${USER_COMMAND%%"${MARKER_END}"}"
3158

32-
echo >&2 " INFO: running: ${COMMAND}"
33-
(
34-
eval "${COMMAND}" </dev/null || true
35-
)
59+
execute_command "${USER_COMMAND}"
3660
else
3761
echo "${line}"
3862
fi

0 commit comments

Comments
 (0)