chore: consolidate new foundation and archive v1 (#1495)
This commit is contained in:
@@ -0,0 +1,283 @@
|
||||
#!/bin/bash
|
||||
# Regression harness for pr-merge.sh Gitea exact-head API path and input safety.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
WORK_ROOT="${AGENT_WORK_ROOT:-${HOME:-/tmp}/mosaic/agent-work}"
|
||||
SANDBOX="$WORK_ROOT/pr-merge-empty-uid-test-$$"
|
||||
MOCK_BIN="$SANDBOX/bin"
|
||||
REPO_DIR="$SANDBOX/repo"
|
||||
HOME_DIR="$SANDBOX/home"
|
||||
LOG_FILE="$SANDBOX/mock.log"
|
||||
|
||||
cleanup() {
|
||||
rm -rf "$SANDBOX"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
mkdir -p "$MOCK_BIN" "$REPO_DIR" "$HOME_DIR"
|
||||
: > "$LOG_FILE"
|
||||
|
||||
cat > "$MOCK_BIN/tea" <<'EOF'
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
printf 'tea %q ' "$@" >> "$PR_MERGE_TEST_LOG"
|
||||
printf '\n' >> "$PR_MERGE_TEST_LOG"
|
||||
if [[ "$*" == *"login list"* ]]; then
|
||||
echo '[{"name":"git.mosaicstack.dev","url":"https://git.mosaicstack.dev"}]'
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$*" == *"pr merge"* ]]; then
|
||||
echo 'user does not exist [uid: 0, name: ]' >&2
|
||||
exit 1
|
||||
fi
|
||||
exit 0
|
||||
EOF
|
||||
chmod +x "$MOCK_BIN/tea"
|
||||
|
||||
cat > "$MOCK_BIN/curl" <<'EOF'
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
printf 'curl %q ' "$@" >> "$PR_MERGE_TEST_LOG"
|
||||
printf '\n' >> "$PR_MERGE_TEST_LOG"
|
||||
args=" $* "
|
||||
out_file=""
|
||||
write_code=false
|
||||
post_data=""
|
||||
prev=""
|
||||
for arg in "$@"; do
|
||||
if [[ "$prev" == "-o" ]]; then
|
||||
out_file="$arg"
|
||||
prev=""
|
||||
continue
|
||||
fi
|
||||
if [[ "$prev" == "data" ]]; then
|
||||
post_data="$arg"
|
||||
[[ "$post_data" == @* ]] && post_data=$(<"${post_data#@}")
|
||||
prev=""
|
||||
continue
|
||||
fi
|
||||
if [[ "$prev" == "config" ]]; then
|
||||
[[ "$arg" == "-" ]] && cat >/dev/null
|
||||
prev=""
|
||||
continue
|
||||
fi
|
||||
case "$arg" in
|
||||
-o) prev="-o" ;;
|
||||
-d|--data|--data-binary) prev="data" ;;
|
||||
-K|--config) prev="config" ;;
|
||||
-w) write_code=true ;;
|
||||
esac
|
||||
done
|
||||
emit_response() {
|
||||
local body="$1"
|
||||
if [[ -n "$out_file" ]]; then
|
||||
printf '%s' "$body" > "$out_file"
|
||||
else
|
||||
printf '%s' "$body"
|
||||
fi
|
||||
if [[ "$write_code" == true ]]; then
|
||||
printf '200'
|
||||
fi
|
||||
}
|
||||
if [[ "$args" == *"/api/v1/repos/mosaicstack/stack/commits/0123456789abcdef0123456789abcdef01234567/status"* ]]; then
|
||||
emit_response '{"state":"success","statuses":[{"context":"ci/test","status":"success"}]}'
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$args" == *"/api/v1/repos/mosaicstack/stack/pulls/123"* && "$args" != *"/api/v1/repos/mosaicstack/stack/pulls/123/merge"* ]]; then
|
||||
emit_response '{"number":123,"title":"mock","state":"open","user":{"login":"tester"},"head":{"ref":"feature/mock","sha":"0123456789abcdef0123456789abcdef01234567","repo":{"full_name":"mosaicstack/stack"}},"base":{"ref":"main"},"labels":[],"assignees":[],"html_url":"https://git.mosaicstack.dev/mosaicstack/stack/pulls/123","mergeable":true}'
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$args" == *"-X POST"* && "$args" == *"/api/v1/repos/mosaicstack/stack/pulls/123/merge"* ]]; then
|
||||
POST_DATA="$post_data" python3 - <<'PY'
|
||||
import json
|
||||
import os
|
||||
payload = json.loads(os.environ["POST_DATA"])
|
||||
assert payload == {
|
||||
"Do": "squash",
|
||||
"head_commit_id": "0123456789abcdef0123456789abcdef01234567",
|
||||
}, payload
|
||||
PY
|
||||
emit_response '{"merged":true,"message":"mock merge complete"}'
|
||||
exit 0
|
||||
fi
|
||||
echo "unexpected curl invocation: $*" >&2
|
||||
exit 97
|
||||
EOF
|
||||
chmod +x "$MOCK_BIN/curl"
|
||||
|
||||
cd "$REPO_DIR"
|
||||
git init -q
|
||||
git remote add origin https://git.mosaicstack.dev/mosaicstack/stack.git
|
||||
# HERMETICITY (#1007) — TWO mechanisms with DIFFERENT jobs; do not conflate them.
|
||||
#
|
||||
# OPERATIVE: the empty repo-local `mosaic.gitIdentity` below. get_gitea_token()
|
||||
# step 0 resolves a per-agent identity from `git config --get mosaic.gitIdentity`,
|
||||
# which on a provisioned agent seat is set GLOBALLY and so leaks into this fresh
|
||||
# repo. Step 0 runs BEFORE the credential loader AND before the GITEA_TOKEN env
|
||||
# check, so the `GITEA_TOKEN=redacted-test-token` exported below is silently
|
||||
# overridden and a REAL per-slot token from $HOME is what flows through the
|
||||
# wrapper. Measured on a provisioned seat before this pin: all 5 mock-curl calls
|
||||
# carried the real per-slot token in argv and the fixture token was never used at
|
||||
# ALL. Three consequences specific to this suite:
|
||||
# 1. pr-merge.sh passes the token as `-H "Authorization: token $token"` and the
|
||||
# mock curl logs full argv, so the real credential is written to $LOG_FILE
|
||||
# on disk — transiently: the suite truncates that file between phases and
|
||||
# the EXIT trap removes $SANDBOX, so it leaves NO post-hoc trace. That is
|
||||
# why this suite was the hardest of the three to detect; observing it needs
|
||||
# an instrument that captures argv while the run is live.
|
||||
# 2. Every failure path dumps $OUTPUT/$LOG_FILE to stderr through
|
||||
# `sed 's/redacted-test-token/***REDACTED***/g'` — a redaction pattern that
|
||||
# is the literal fixture string and therefore CANNOT match the token
|
||||
# actually in use.
|
||||
# 3. The leak assertion at "Token leaked to pr-merge.sh output" greps for that
|
||||
# same fixture string, so on a provisioned seat it passes vacuously: it is
|
||||
# searching for a value the run never used.
|
||||
# An empty repo-local value shadows the global one and reads back empty at rc=0.
|
||||
#
|
||||
# CONTAINMENT: the sandboxed HOME exported below. It only has to bound a failure
|
||||
# that the pin should already have prevented.
|
||||
#
|
||||
# NOTE FOR ANYONE AUDITING THIS SUITE: the sandboxed HOME is containment, NOT an
|
||||
# assay. Running a suite under a decoy HOME to test for this defect REMOVES the
|
||||
# trigger — ~/.gitconfig is where the global identity lives, so step 0 is skipped
|
||||
# by construction and every suite reads clean however vulnerable it is. To measure,
|
||||
# REPLICATE a seat (a decoy HOME whose .gitconfig sets mosaic.gitIdentity, with no
|
||||
# per-slot token) so step 0 reaches its fail-loud branch.
|
||||
#
|
||||
# Note the env-var route does NOT work: detect-platform.sh reads
|
||||
# "${MOSAIC_GIT_IDENTITY:-}", and `:-` treats set-but-empty identically to unset.
|
||||
git -C "$REPO_DIR" config mosaic.gitIdentity ""
|
||||
|
||||
# $SANDBOX/$HOME_DIR were derived from the real $HOME above, before this export.
|
||||
export HOME="$HOME_DIR"
|
||||
export PATH="$MOCK_BIN:$PATH"
|
||||
export PR_MERGE_TEST_LOG="$LOG_FILE"
|
||||
export GITEA_LOGIN="git.mosaicstack.dev"
|
||||
export GITEA_URL="https://git.mosaicstack.dev"
|
||||
export GITEA_TOKEN="redacted-test-token"
|
||||
|
||||
OUTPUT="$SANDBOX/output.log"
|
||||
if ! "$SCRIPT_DIR/pr-merge.sh" -n 123 -m squash > "$OUTPUT" 2>&1; then
|
||||
echo "Expected pr-merge.sh to use the exact-head Gitea API path." >&2
|
||||
echo "--- output ---" >&2
|
||||
sed 's/redacted-test-token/***REDACTED***/g' "$OUTPUT" >&2
|
||||
echo "--- mock log ---" >&2
|
||||
sed 's/redacted-test-token/***REDACTED***/g' "$LOG_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! grep -q '/api/v1/repos/mosaicstack/stack/pulls/123/merge' "$LOG_FILE"; then
|
||||
echo "Expected authenticated Gitea merge API endpoint to be called." >&2
|
||||
sed 's/redacted-test-token/***REDACTED***/g' "$LOG_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if grep -q 'redacted-test-token' "$OUTPUT"; then
|
||||
echo "Token leaked to pr-merge.sh output." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cat > "$MOCK_BIN/tea" <<'EOF'
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
printf 'tea %q ' "$@" >> "$PR_MERGE_TEST_LOG"
|
||||
printf '\n' >> "$PR_MERGE_TEST_LOG"
|
||||
if [[ "$*" == *"login list"* ]]; then
|
||||
echo '[]'
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$*" == *"pr merge"* ]]; then
|
||||
echo 'tea merge should not run without a configured host login' >&2
|
||||
exit 99
|
||||
fi
|
||||
exit 0
|
||||
EOF
|
||||
chmod +x "$MOCK_BIN/tea"
|
||||
unset GITEA_LOGIN
|
||||
: > "$LOG_FILE"
|
||||
if ! "$SCRIPT_DIR/pr-merge.sh" -n 123 -m squash > "$OUTPUT" 2>&1; then
|
||||
echo "Expected the exact-head API path not to depend on a tea login." >&2
|
||||
sed 's/redacted-test-token/***REDACTED***/g' "$OUTPUT" >&2
|
||||
sed 's/redacted-test-token/***REDACTED***/g' "$LOG_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! grep -q '/api/v1/repos/mosaicstack/stack/pulls/123/merge' "$LOG_FILE"; then
|
||||
echo "Expected missing tea login path to call Gitea API merge endpoint." >&2
|
||||
sed 's/redacted-test-token/***REDACTED***/g' "$LOG_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SENTINEL="$SANDBOX/injected-sentinel"
|
||||
INJECTION="123; touch $SENTINEL #"
|
||||
|
||||
cat > "$MOCK_BIN/gh" <<'EOF'
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
printf 'gh %q ' "$@" >> "$PR_MERGE_TEST_LOG"
|
||||
printf '\n' >> "$PR_MERGE_TEST_LOG"
|
||||
if [[ "$*" == *"pr view"* ]]; then
|
||||
cat <<'JSON'
|
||||
{"number":123,"title":"mock","baseRefName":"main","headRefName":"feature/mock"}
|
||||
JSON
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$*" == *"pr merge"* ]]; then
|
||||
exit 0
|
||||
fi
|
||||
echo "unexpected gh invocation: $*" >&2
|
||||
exit 98
|
||||
EOF
|
||||
chmod +x "$MOCK_BIN/gh"
|
||||
|
||||
cd "$REPO_DIR"
|
||||
git remote set-url origin https://github.com/mosaicstack/stack.git
|
||||
: > "$LOG_FILE"
|
||||
rm -f "$SENTINEL"
|
||||
if "$SCRIPT_DIR/pr-merge.sh" -n "$INJECTION" -m squash > "$OUTPUT" 2>&1; then
|
||||
echo "Expected GitHub metacharacter PR number to be rejected." >&2
|
||||
sed 's/redacted-test-token/***REDACTED***/g' "$OUTPUT" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ -e "$SENTINEL" ]]; then
|
||||
echo "GitHub metacharacter PR number executed injected shell command." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ -s "$LOG_FILE" ]]; then
|
||||
echo "GitHub metacharacter PR number should be rejected before gh calls." >&2
|
||||
sed 's/redacted-test-token/***REDACTED***/g' "$LOG_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! grep -q 'Invalid PR number' "$OUTPUT"; then
|
||||
echo "Expected invalid PR number error for GitHub metacharacter input." >&2
|
||||
sed 's/redacted-test-token/***REDACTED***/g' "$OUTPUT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$REPO_DIR"
|
||||
git remote set-url origin https://git.mosaicstack.dev/mosaicstack/stack.git
|
||||
export GITEA_LOGIN="git.mosaicstack.dev"
|
||||
: > "$LOG_FILE"
|
||||
rm -f "$SENTINEL"
|
||||
if "$SCRIPT_DIR/pr-merge.sh" -n "$INJECTION" -m squash > "$OUTPUT" 2>&1; then
|
||||
echo "Expected Gitea metacharacter PR number to be rejected." >&2
|
||||
sed 's/redacted-test-token/***REDACTED***/g' "$OUTPUT" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ -e "$SENTINEL" ]]; then
|
||||
echo "Gitea metacharacter PR number executed injected shell command." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ -s "$LOG_FILE" ]]; then
|
||||
echo "Gitea metacharacter PR number should be rejected before tea/curl calls." >&2
|
||||
sed 's/redacted-test-token/***REDACTED***/g' "$LOG_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! grep -q 'Invalid PR number' "$OUTPUT"; then
|
||||
echo "Expected invalid PR number error for Gitea metacharacter input." >&2
|
||||
sed 's/redacted-test-token/***REDACTED***/g' "$OUTPUT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "pr-merge.sh Gitea exact-head API regression passed"
|
||||
Reference in New Issue
Block a user