Co-authored-by: jason.woltje <jason@diversecanvas.com> Co-committed-by: jason.woltje <jason@diversecanvas.com>
218 lines
9.6 KiB
Bash
Executable File
218 lines
9.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Regression harness for detect-platform.sh's get_gitea_token() per-agent
|
|
# identity resolution (Gate-16 author≠reviewer separation) — the API-tooling
|
|
# counterpart to git-credential-mosaic, so pr-create.sh/issue-create.sh/etc.
|
|
# open records under the resolved agent identity, not the shared account.
|
|
#
|
|
# Covers:
|
|
# 1. Identity resolution priority: MOSAIC_GIT_IDENTITY env > git config
|
|
# mosaic.gitIdentity (per-worktree).
|
|
# 2. Correct per-slot token file path chosen per host
|
|
# (gitea-usc-<id>.token vs gitea-mosaicstack-<id>.token).
|
|
# 3. Per-slot token present -> that token is returned (agent-authored calls).
|
|
# 4. No identity requested -> shared credential-loader token (backward
|
|
# compat, unchanged).
|
|
# 5. Patch 2b — explicit identity + recognized Gitea host + ABSENT per-slot
|
|
# token for that identity -> FAIL LOUD (nonzero return, empty stdout, a
|
|
# stderr diagnostic naming identity/source/host/expected path). Must NOT
|
|
# fall through to the shared/default token (Gate-16 author≠reviewer
|
|
# integrity — never silently borrow another slot's credentials). Covered
|
|
# for both identity sources (git config, MOSAIC_GIT_IDENTITY env) and
|
|
# for a same-identity cross-host case (token exists for one host, not
|
|
# the other).
|
|
# 6. Scope containment: identity requested + an UNRECOGNIZED Gitea host (no
|
|
# per-slot token scheme) -> Patch 2b does not apply; existing
|
|
# fall-through behavior is unchanged.
|
|
#
|
|
# Uses a stubbed credentials.json + stubbed per-slot token files under a fake
|
|
# HOME. NEVER reads real secrets or touches the real ~/.config/mosaic/secrets.
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
WORK_DIR="${MOSAIC_TEST_WORK_DIR:-$PWD/.mosaic-test-work/gitea-token-identity}"
|
|
FAKE_HOME="$WORK_DIR/home"
|
|
REPO_DIR="$WORK_DIR/repo"
|
|
CREDENTIALS_FILE="$FAKE_HOME/.config/mosaic/credentials.json"
|
|
|
|
rm -rf "$WORK_DIR"
|
|
mkdir -p "$FAKE_HOME/.config/mosaic/secrets/gitea-tokens" "$REPO_DIR"
|
|
|
|
git -C "$REPO_DIR" init -q
|
|
git -C "$REPO_DIR" remote add origin https://git.mosaicstack.dev/mosaicstack/stack.git
|
|
|
|
cat > "$CREDENTIALS_FILE" <<'JSON'
|
|
{
|
|
"gitea": {
|
|
"mosaicstack": {
|
|
"url": "https://git.mosaicstack.dev",
|
|
"token": "shared-mosaicstack-token"
|
|
},
|
|
"usc": {
|
|
"url": "https://git.uscllc.com",
|
|
"token": "shared-usc-token"
|
|
}
|
|
}
|
|
}
|
|
JSON
|
|
|
|
fail=0
|
|
assert_eq() {
|
|
local desc="$1" expected="$2" actual="$3"
|
|
if [[ "$expected" != "$actual" ]]; then
|
|
echo "FAIL: $desc — expected '$expected', got '$actual'" >&2
|
|
fail=1
|
|
fi
|
|
}
|
|
|
|
# Runs get_gitea_token for $1=host inside REPO_DIR (per-worktree git config
|
|
# resolves there) with a fake HOME + the stub credentials.json, plus any
|
|
# extra env passed in $@.
|
|
call_get_gitea_token() {
|
|
local host="$1"; shift
|
|
(
|
|
cd "$REPO_DIR"
|
|
# shellcheck disable=SC2016 # deliberately deferred: $DETECT_PLATFORM_SH is
|
|
# expanded by the INNER bash -c (via the exported env var below), not here.
|
|
env -i HOME="$FAKE_HOME" PATH="$PATH" MOSAIC_CREDENTIALS_FILE="$CREDENTIALS_FILE" \
|
|
DETECT_PLATFORM_SH="$SCRIPT_DIR/detect-platform.sh" "$@" \
|
|
bash -c 'source "$DETECT_PLATFORM_SH"; get_gitea_token "$1"' _ "$host"
|
|
)
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 1. No identity resolvable -> shared credential-loader token (unchanged).
|
|
# ---------------------------------------------------------------------------
|
|
git -C "$REPO_DIR" config --unset mosaic.gitIdentity 2>/dev/null || true
|
|
out=$(call_get_gitea_token "git.mosaicstack.dev")
|
|
assert_eq "shared fallback (no identity)" "shared-mosaicstack-token" "$out"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 2. git config mosaic.gitIdentity resolves to an agent WITH a per-slot
|
|
# token -> that token wins over the shared account.
|
|
# ---------------------------------------------------------------------------
|
|
echo -n "agentA-mosaicstack-token" > "$FAKE_HOME/.config/mosaic/secrets/gitea-tokens/gitea-mosaicstack-agentA.token"
|
|
git -C "$REPO_DIR" config mosaic.gitIdentity agentA
|
|
out=$(call_get_gitea_token "git.mosaicstack.dev")
|
|
assert_eq "git-config identity token" "agentA-mosaicstack-token" "$out"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 3. MOSAIC_GIT_IDENTITY env beats git config mosaic.gitIdentity.
|
|
# ---------------------------------------------------------------------------
|
|
echo -n "agentB-mosaicstack-token" > "$FAKE_HOME/.config/mosaic/secrets/gitea-tokens/gitea-mosaicstack-agentB.token"
|
|
out=$(call_get_gitea_token "git.mosaicstack.dev" MOSAIC_GIT_IDENTITY=agentB)
|
|
assert_eq "env beats git-config identity token" "agentB-mosaicstack-token" "$out"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 4. FAIL LOUD (Patch 2b): an identity is explicitly requested (via git config
|
|
# mosaic.gitIdentity, and separately via MOSAIC_GIT_IDENTITY env) for a
|
|
# RECOGNIZED Gitea host, but no per-slot token exists for THAT identity.
|
|
# Must NOT fall through to the shared/default token — silently borrowing
|
|
# another slot's credentials would post PRs/issues/reviews as the WRONG
|
|
# agent (Gate-16 author≠reviewer integrity break). Expect: nonzero return,
|
|
# EMPTY stdout (no token — shared or otherwise — leaked), and a stderr
|
|
# diagnostic naming the identity, its source, the host, and the expected
|
|
# per-slot token path.
|
|
# ---------------------------------------------------------------------------
|
|
assert_failloud() {
|
|
local desc="$1" host="$2" ident="$3" expected_tok_path="$4"; shift 4
|
|
local stderr_file="$WORK_DIR/stderr.tmp"
|
|
: > "$stderr_file"
|
|
set +e
|
|
local stdout
|
|
stdout=$(call_get_gitea_token "$host" "$@" 2>"$stderr_file")
|
|
local rc=$?
|
|
set -e
|
|
local stderr
|
|
stderr=$(cat "$stderr_file")
|
|
if [[ "$rc" -eq 0 ]]; then
|
|
echo "FAIL: $desc — expected nonzero return, got 0 (stdout='$stdout')" >&2
|
|
fail=1
|
|
fi
|
|
if [[ -n "$stdout" ]]; then
|
|
echo "FAIL: $desc — expected empty stdout (no token leaked), got '$stdout'" >&2
|
|
fail=1
|
|
fi
|
|
if [[ "$stderr" != *"$ident"* ]]; then
|
|
echo "FAIL: $desc — stderr does not name the requested identity '$ident':" >&2
|
|
echo "$stderr" >&2
|
|
fail=1
|
|
fi
|
|
if [[ "$stderr" != *"$host"* ]]; then
|
|
echo "FAIL: $desc — stderr does not name the host '$host':" >&2
|
|
echo "$stderr" >&2
|
|
fail=1
|
|
fi
|
|
if [[ "$stderr" != *"$expected_tok_path"* ]]; then
|
|
echo "FAIL: $desc — stderr does not name the expected per-slot token path '$expected_tok_path':" >&2
|
|
echo "$stderr" >&2
|
|
fail=1
|
|
fi
|
|
if [[ "$stderr" == *"shared"*"token"* ]]; then
|
|
echo "FAIL: $desc — stderr unexpectedly mentions a shared token value:" >&2
|
|
echo "$stderr" >&2
|
|
fail=1
|
|
fi
|
|
}
|
|
|
|
# 4a. git config mosaic.gitIdentity source, recognized host (mosaicstack),
|
|
# shared token IS present but must not be borrowed.
|
|
git -C "$REPO_DIR" config mosaic.gitIdentity no-such-agent
|
|
assert_failloud "fail-loud via git-config identity (recognized host)" \
|
|
"git.mosaicstack.dev" "no-such-agent" \
|
|
"$FAKE_HOME/.config/mosaic/secrets/gitea-tokens/gitea-mosaicstack-no-such-agent.token"
|
|
git -C "$REPO_DIR" config --unset mosaic.gitIdentity
|
|
|
|
# 4b. MOSAIC_GIT_IDENTITY env source (takes priority over git config), same
|
|
# recognized-host / absent-token scenario -> also fails loud.
|
|
assert_failloud "fail-loud via MOSAIC_GIT_IDENTITY env (recognized host)" \
|
|
"git.mosaicstack.dev" "no-such-agent-env" \
|
|
"$FAKE_HOME/.config/mosaic/secrets/gitea-tokens/gitea-mosaicstack-no-such-agent-env.token" \
|
|
MOSAIC_GIT_IDENTITY=no-such-agent-env
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 5. Correct per-slot token PATH per host: same agent id, only a usc token
|
|
# exists. usc host returns it (happy path, unchanged). mosaicstack host
|
|
# has NO per-slot token for this identity -> Patch 2b fail-loud applies
|
|
# there too (must NOT fall back to the shared mosaicstack token, and must
|
|
# NOT leak the agent's usc token either).
|
|
# ---------------------------------------------------------------------------
|
|
echo -n "agentD-usc-token" > "$FAKE_HOME/.config/mosaic/secrets/gitea-tokens/gitea-usc-agentD.token"
|
|
git -C "$REPO_DIR" config mosaic.gitIdentity agentD
|
|
out=$(call_get_gitea_token "git.uscllc.com")
|
|
assert_eq "host-scoped token path (usc)" "agentD-usc-token" "$out"
|
|
assert_failloud "fail-loud on cross-host absence (no fallback, no cross-host leak)" \
|
|
"git.mosaicstack.dev" "agentD" \
|
|
"$FAKE_HOME/.config/mosaic/secrets/gitea-tokens/gitea-mosaicstack-agentD.token"
|
|
git -C "$REPO_DIR" config --unset mosaic.gitIdentity
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 6. Scope containment: identity explicitly requested, but the host is NOT a
|
|
# recognized Gitea host (no per-slot token scheme at all) -> Patch 2b does
|
|
# NOT apply; existing fall-through behavior is unchanged (ends in the
|
|
# pre-existing generic failure since no shared credentials match either,
|
|
# NOT the fail-loud diagnostic path).
|
|
# ---------------------------------------------------------------------------
|
|
git -C "$REPO_DIR" config mosaic.gitIdentity no-such-agent
|
|
set +e
|
|
out=$(call_get_gitea_token "github.com" 2>"$WORK_DIR/stderr-scope.tmp")
|
|
rc=$?
|
|
set -e
|
|
err=$(cat "$WORK_DIR/stderr-scope.tmp")
|
|
if [[ "$rc" -eq 0 ]]; then
|
|
echo "FAIL: unrecognized host + identity — expected nonzero (no credentials configured), got 0" >&2
|
|
fail=1
|
|
fi
|
|
if [[ "$err" == *"no per-slot token at"* ]]; then
|
|
echo "FAIL: unrecognized host + identity — fail-loud diagnostic must not fire for a host with no per-slot scheme:" >&2
|
|
echo "$err" >&2
|
|
fail=1
|
|
fi
|
|
git -C "$REPO_DIR" config --unset mosaic.gitIdentity
|
|
|
|
if [[ "$fail" -eq 0 ]]; then
|
|
echo "get_gitea_token identity resolution regression passed"
|
|
fi
|
|
|
|
exit "$fail"
|