ci/woodpecker/pr/ci Pipeline was canceled
test-issue-create-body-safety.sh and test-issue-create-interactive-auth.sh inherited the seat's real HOME and global git config. With the #1280 fix activating identity mode BEFORE the tea path, a workstation-global mosaic.gitIdentity resolved inside the fixture repo, and the wrapper's API fallback posted to the LIVE forge with a real per-slot token — six real issues (#1282-#1287, authored mos-dt-0, closed with provenance by fred within the hour). Neutralize the source the resolver actually reads, and prove it by making the resolution fail. A control that does not make the thing fail has not been shown to control it. The earlier attempted neutralization pinned MOSAIC_CREDENTIALS_FILE to a fake — a real guard aimed at an adjacent input: the identity arm reads the per-slot token file directly and never consults credentials.json. Hence env -i with a fake HOME and GIT_CONFIG_GLOBAL=/dev/null (severing the global identity) rather than one more targeted variable, plus a curl tripwire stub in the body-safety harness so ANY provider request is a loud test failure instead of a live write.
126 lines
4.3 KiB
Bash
Executable File
126 lines
4.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# Regression harness for issue-create.sh Markdown-body safety (#559).
|
||
#
|
||
# Guards against reintroduction of eval-based command construction. The wrapper
|
||
# builds its tea/gh invocation as an argv array, so a body containing command
|
||
# substitution ($(...)), backticks, quotes, and dollar signs MUST reach tea
|
||
# verbatim and MUST NOT be shell-evaluated. This test asserts both:
|
||
# 1. No command-substitution side effect (an injected `touch SENTINEL` never runs).
|
||
# 2. The --description value tea receives is byte-for-byte the original body.
|
||
|
||
set -euo pipefail
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
WORK_DIR="${MOSAIC_TEST_WORK_DIR:-$PWD/.mosaic-test-work/issue-create-body-safety}"
|
||
REPO_DIR="$WORK_DIR/repo"
|
||
BIN_DIR="$WORK_DIR/bin"
|
||
SENTINEL="$WORK_DIR/INJECTION_SENTINEL"
|
||
BODY_FILE="$WORK_DIR/body.txt"
|
||
RECEIVED_FILE="$WORK_DIR/received-description.txt"
|
||
|
||
rm -rf "$WORK_DIR"
|
||
mkdir -p "$REPO_DIR" "$BIN_DIR"
|
||
|
||
git -C "$REPO_DIR" init -q
|
||
git -C "$REPO_DIR" remote add origin https://git.mosaicstack.dev/mosaicstack/stack.git
|
||
|
||
# Hostile Markdown body. The unquoted heredoc expands $SENTINEL (a real path we
|
||
# want embedded) but every shell metacharacter we care about is backslash-escaped
|
||
# so the TEST shell writes them literally into the file — the bytes the wrapper
|
||
# must then preserve.
|
||
cat > "$BODY_FILE" <<EOF
|
||
# Release notes
|
||
|
||
Inline code: \`rm -rf /\` must stay literal.
|
||
Command sub attempt: \$(touch $SENTINEL)
|
||
Backtick cmd attempt: \`touch $SENTINEL\`
|
||
Dollars: \$HOME \${PATH} \$5.00 and 100% done
|
||
Quotes: "double" and 'single' and \`mixed\`
|
||
Trailing pipe-ish: foo | bar && baz ; qux
|
||
EOF
|
||
|
||
BODY="$(cat "$BODY_FILE")"
|
||
|
||
# Mock tea: resolve a mosaicstack login, then capture the --description verbatim.
|
||
cat > "$BIN_DIR/tea" <<'SH'
|
||
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
|
||
if [[ "$*" == "login list --output json" ]]; then
|
||
cat <<'JSON'
|
||
[
|
||
{"name":"mosaicstack","url":"https://git.mosaicstack.dev","user":"ci-bot"}
|
||
]
|
||
JSON
|
||
exit 0
|
||
fi
|
||
|
||
if [[ "${1:-}" == "api" ]]; then
|
||
printf '%s\n' '{"login":"ci-bot"}'
|
||
exit 0
|
||
fi
|
||
|
||
if [[ "${1:-}" == "issue" && "${2:-}" == "create" ]]; then
|
||
desc=""
|
||
while [[ $# -gt 0 ]]; do
|
||
case "$1" in
|
||
--description) desc="$2"; shift 2 ;;
|
||
*) shift ;;
|
||
esac
|
||
done
|
||
printf '%s' "$desc" > "$MOSAIC_TEST_RECEIVED"
|
||
echo "#1 created"
|
||
exit 0
|
||
fi
|
||
|
||
exit 0
|
||
SH
|
||
chmod +x "$BIN_DIR/tea"
|
||
|
||
# TRIPWIRE provider stub: this harness tests argv construction, so ANY curl
|
||
# call is a failure of that contract (and, before this stub existed, a LIVE
|
||
# write — the #1282–#1287 incident: the seat's real HOME leaked a global
|
||
# mosaic.gitIdentity, flipping the wrapper into identity mode whose real
|
||
# per-slot token created real issues on the forge). Fail loudly instead.
|
||
cat > "$BIN_DIR/curl" <<'SH'
|
||
#!/usr/bin/env bash
|
||
echo "FAIL: body-safety harness reached a provider request — this test must never curl" >&2
|
||
exit 99
|
||
SH
|
||
chmod +x "$BIN_DIR/curl"
|
||
|
||
# Hermetic invocation: fake HOME (no credentials, no tea config, no token
|
||
# slots) and GIT_CONFIG_GLOBAL severed — `git config --get mosaic.gitIdentity`
|
||
# otherwise resolves the WORKSTATION's global identity (mos-dt-0 on the seat
|
||
# that wrote this) and reroutes the wrapper into identity mode (#1280 family).
|
||
(
|
||
cd "$REPO_DIR"
|
||
env -i HOME="$WORK_DIR/home" PATH="$BIN_DIR:$PATH" \
|
||
GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=/dev/null \
|
||
MOSAIC_TEST_RECEIVED="$RECEIVED_FILE" \
|
||
"$SCRIPT_DIR/issue-create.sh" -t "Body safety test" -b "$BODY"
|
||
) >/dev/null
|
||
mkdir -p "$WORK_DIR/home"
|
||
|
||
# 1. No command substitution executed anywhere in the pipeline.
|
||
if [[ -e "$SENTINEL" ]]; then
|
||
echo "FAIL: injected command substitution executed (sentinel file created): $SENTINEL" >&2
|
||
exit 1
|
||
fi
|
||
|
||
# 2. tea actually received the body (issue create path taken, not silently dropped).
|
||
if [[ ! -f "$RECEIVED_FILE" ]]; then
|
||
echo "FAIL: tea issue create was never invoked with a --description" >&2
|
||
exit 1
|
||
fi
|
||
|
||
# 3. The description tea received is byte-for-byte the original body.
|
||
if [[ "$(cat "$RECEIVED_FILE")" != "$BODY" ]]; then
|
||
echo "FAIL: body was not preserved verbatim through issue-create.sh" >&2
|
||
echo "--- expected ---" >&2; printf '%s\n' "$BODY" >&2
|
||
echo "--- received ---" >&2; cat "$RECEIVED_FILE" >&2
|
||
exit 1
|
||
fi
|
||
|
||
echo "issue-create.sh Markdown body-safety regression harness passed"
|