fix(mosaic): harden credential lifecycle boundaries

This commit is contained in:
2026-08-05 18:01:41 -05:00
parent 26203bd92c
commit e46e114d3b
13 changed files with 127 additions and 31 deletions
@@ -539,7 +539,7 @@ get_gitea_token() {
if [[ -n "$_idpfx" ]]; then
local _idtok="$HOME/.config/mosaic/secrets/gitea-tokens/${_idpfx}-${_ident}.token"
local _idcred="$HOME/.config/mosaic/secrets/gitea-tokens/${_idpfx}-${_ident}.credential.json"
if [[ -r "$_idcred" ]]; then
if [[ -e "$_idcred" || -L "$_idcred" ]]; then
local _resolved_token
_resolved_token=$(python3 "$script_dir/resolve-credential-envelope.py" \
"$_idcred" "$_ident" "${MOSAIC_CREDENTIAL_ESTATE:-}" "$host") || return 1
@@ -38,25 +38,25 @@ trace_resolution() {
# survives across non-persistent shells) > git-supplied username (credential.username
# / URL). When the resolved identity has a matching per-agent token, use it instead of
# the shared account. Backward-compatible: nothing resolvable → shared token.
case "$host" in
git.uscllc.com) idpfx=gitea-usc;;
git.mosaicstack.dev) idpfx=gitea-mosaicstack;;
*) idpfx="";;
esac
ident="$MOSAIC_GIT_IDENTITY"
[ -z "$ident" ] && ident=$(git config --get mosaic.gitIdentity 2>/dev/null)
[ -z "$ident" ] && ident="$username_in"
if [ -n "${MOSAIC_AGENT_NAME:-}" ] && [ -n "$ident" ] && [ "$ident" != "$MOSAIC_AGENT_NAME" ]; then
if [ -n "$idpfx" ] && [ -n "${MOSAIC_AGENT_NAME:-}" ] && [ -n "$ident" ] && [ "$ident" != "$MOSAIC_AGENT_NAME" ]; then
echo "quit=true"
printf 'MOSAIC_CREDENTIAL_REFUSAL outcome=refused reason=provider-identity-mismatch identity=%s fleet_identity=%s host=%s shared_path_entered=false source=git-credential-mosaic\n' \
"$ident" "$MOSAIC_AGENT_NAME" "$host" >&2
exit 1
fi
if [ -n "$ident" ]; then
case "$host" in
git.uscllc.com) idpfx=gitea-usc;;
git.mosaicstack.dev) idpfx=gitea-mosaicstack;;
*) idpfx="";;
esac
if [ -n "$idpfx" ]; then
idtok="$HOME/.config/mosaic/secrets/gitea-tokens/${idpfx}-${ident}.token"
idcred="$HOME/.config/mosaic/secrets/gitea-tokens/${idpfx}-${ident}.credential.json"
if [ -r "$idcred" ]; then
if [ -e "$idcred" ] || [ -L "$idcred" ]; then
token=$(python3 "$script_dir/resolve-credential-envelope.py" \
"$idcred" "$ident" "${MOSAIC_CREDENTIAL_ESTATE:-}" "$host") || exit 1
trace_resolution identity credential-resolved "$ident" "$host" git-credential-mosaic
@@ -194,6 +194,7 @@ PY
chmod 600 "$envelope"
out=$(run_helper "git.mosaicstack.dev" "agentE" MOSAIC_AGENT_NAME=agentE MOSAIC_CREDENTIAL_ESTATE=homelab)
assert_eq "governed envelope: password" "password=agentE-envelope-token" "$(echo "$out" | grep '^password=')"
echo -n "must-not-fallback-legacy" > "$FAKE_HOME/.config/mosaic/secrets/gitea-tokens/gitea-mosaicstack-agentE.token"
chmod 640 "$envelope"
set +e
out=$(run_helper "git.mosaicstack.dev" "agentE" MOSAIC_AGENT_NAME=agentE MOSAIC_CREDENTIAL_ESTATE=homelab 2>"$WORK_DIR/envelope-mode.stderr")
@@ -210,6 +211,8 @@ fi
# ---------------------------------------------------------------------------
out=$(run_helper "github.com" "agentA")
assert_eq "unknown host: no output" "" "$out"
out=$(run_helper "github.com" "github-user" MOSAIC_AGENT_NAME=agentA)
assert_eq "unknown host in fleet context: no output" "" "$out"
# ---------------------------------------------------------------------------
# 9. Non-"get" verb (store/erase) -> exit 0, no output (git-credential