fix: harden entrypoint output handling in fullsend action - #230
fix: harden entrypoint output handling in fullsend action#230waynesun09 wants to merge 2 commits into
Conversation
The run-fullsend step used `echo fullsend entrypoint` which prints the literal string instead of executing the CLI, making artifact upload non-functional. Remove `echo` so the command actually runs. Also add path validation (must be absolute) and use a heredoc delimiter for GITHUB_OUTPUT to prevent injection via newlines in CLI output. Signed-off-by: Wayne Sun <gsun@redhat.com>
The echo is intentional — fullsend CLI does not have an entrypoint subcommand yet. Revert the echo removal while keeping the path validation and GITHUB_OUTPUT hardening fixes. Signed-off-by: Wayne Sun <gsun@redhat.com>
ralphbean
left a comment
There was a problem hiding this comment.
Security hardening review — three issues to address before merging.
The approach is sound (heredoc delimiter for GITHUB_OUTPUT, absolute path validation), but there are gaps that leave the same injection vectors partially open. See inline comments.
| echo "artifact_dir<<FULLSEND_EOF" | ||
| echo "${ARTIFACT_DIR}" | ||
| echo "FULLSEND_EOF" | ||
| } >> "${GITHUB_OUTPUT}" |
There was a problem hiding this comment.
Delimiter injection / no newline validation
The static FULLSEND_EOF delimiter can be forged if an attacker controls CLI stdout. If ARTIFACT_DIR contains FULLSEND_EOF on its own line, the heredoc terminates early and arbitrary GITHUB_OUTPUT variables can be injected — the same class of attack this PR is trying to prevent.
More fundamentally, ARTIFACT_DIR is not validated for embedded newlines or carriage returns, which is the primary injection vector.
Suggested fix (both layers):
# Reject paths with newlines/CRs
if [[ "${ARTIFACT_DIR}" =~ $'\n' || "${ARTIFACT_DIR}" =~ $'\r' ]]; then
echo "::error::fullsend entrypoint returned a path containing control characters"
exit 1
fi
# Use a randomized delimiter
DELIMITER="FULLSEND_EOF_$(head -c 16 /dev/urandom | xxd -p)"
{
echo "artifact_dir<<${DELIMITER}"
echo "${ARTIFACT_DIR}"
echo "${DELIMITER}"
} >> "${GITHUB_OUTPUT}"The newline rejection is the most important fix — it neutralizes delimiter collision as a secondary effect.
| echo "artifact_dir=${ARTIFACT_DIR}" >> "${GITHUB_OUTPUT}" | ||
| # Validate path is absolute and within expected boundaries. | ||
| if [[ "${ARTIFACT_DIR}" != /* ]]; then | ||
| echo "::error::fullsend entrypoint returned a non-absolute path: ${ARTIFACT_DIR}" |
There was a problem hiding this comment.
Untrusted input in ::error:: annotation
ARTIFACT_DIR comes from CLI stdout and is untrusted. Interpolating it directly into a ::error:: workflow command could allow workflow command injection (e.g., if the value contains :: sequences or encoded newlines).
Suggested fix — log the value separately instead of interpolating it into the annotation:
echo "::error::fullsend entrypoint returned a non-absolute path (see logs)"
printf 'Rejected path value: %s\n' "${ARTIFACT_DIR}"| # Validate path is absolute and within expected boundaries. | ||
| if [[ "${ARTIFACT_DIR}" != /* ]]; then | ||
| echo "::error::fullsend entrypoint returned a non-absolute path: ${ARTIFACT_DIR}" | ||
| exit 1 |
There was a problem hiding this comment.
No path canonicalization
A path like /tmp/../../../etc/shadow passes the absolute-path check. Consider canonicalizing with realpath and optionally enforcing a prefix:
ARTIFACT_DIR="$(realpath -m "${ARTIFACT_DIR}")"The -m flag resolves logically without requiring the path to exist. This would also pair well with a prefix check (e.g., must be under ${GITHUB_WORKSPACE} or ${RUNNER_TEMP}) for defense-in-depth.
|
Closing — the underlying pattern this PR was hardening no longer exists on The action now uses All three review findings from @ralphbean were valid for the old |
Summary
actions/upload-artifact=assignment, preventing newline injection from CLI stdoutNote: the
echoinARTIFACT_DIR="$(echo fullsend entrypoint ...)"is an intentional stub — thefullsend entrypointCLI subcommand is not yet implemented (see alsointernal/layers/workflows.go:175). These hardening fixes prepare the action for when the real command replaces the stub.Test plan
Hardens output handling introduced in #210.