credentials.sh: load_credentials returns rc=0 without reading the file when env vars are pre-set (cross-instance gitea credential bleed) #981
Open
opened 2026-07-31 06:15:49 +00:00 by mos-dt-0
·
0 comments
No Branch/Tag Specified
main
feat/1050-install-state-machine-red-fixture
fix/1043-pane-git-identity
fix/pr-merge-message-field
feat/1051-mosaic-brain-installer
feat/1045-mosaic-cred
remediation/state
fix/1056-upgrade-rollback-control-race
fix/1019-ci-queue-timeout-harness
next
feat/rm-02-gate-registry
fix/rm-01-reproducible-checkout
remediation/mission-setup
fix/hygiene-inert-format-gate
fix/1019-queue-guard-stdin
feat/mos-ste-writing-standard
fix/1007-suite-hermeticity
fix/991-comment-url-scheme-normalise
feat/push-guard-null-case-verification
mos-comms-live
docs/heartbeat-framework-layering-ms-lead
feat/869-c4-version-coupling
feat/869-c2-install-ordering-guard
feat/869-c5-doctor-activation-check
feat/per-agent-gitea-identity
fix/875-belongs-case-insensitive-slug
fix/ci-queue-wait-404-branch-absent
feat/869-c1-activation-probe
feat/869-c3-broker-supervisor
fix/865-tea-cli-comment-invocation
feat/glpi-skills
fix/860-deflake-mutator-lease-gate
fix/850-detect-platform-port-normalization
fix/856-worktree-deps-preflight
fix/835-pr-review-approve-reject-comment-flag
fix/848-truthful-evidence
fix/812-pr-review-comment
fix/849-recovery-runtime-fixture-race
docs/758-ledger-m5-001-sync
feat/834-tc-server-side-doc
feat/833-constrained-recovery-command
feat/827-gate0-probe
governance/gate0-probe3-amendment
fix/795-codex-pr-diff
fix/795-ci-base-jq
fix/795-ci-base-git
feat/791-pr3-fleet-regen
feat/791-pr2-snapshot-restore
fix/807-glpi-206
fix/808-agent-send-false-sender
feat/791-upgrade-config-protection
feat/790-mosaic-yolo-claudex-pr2
feat/790-mosaic-yolo-claudex
feat/758-v1-v2-migrator
fix/766-exact-fleet-comms
test/758-reconciler-lifecycle-gates
docs/771-kbn101-db-role-split
test/758-example-profile-dispositions
feat/758-shared-role-resolution
feat/mos-logical-identity-fencing
feat/769-kbn100-unified-schema
docs/753-kbn010-threat-gate
feat/758-roster-v2-compiler
feat/756-official-discord-plugin
docs/758-fleet-config-management
fix/mos-option2-qualification-format
docs/issue-758-m0
docs/mos-option2-qualification
mos-comms
feat/tess-interaction-agent
fix/tess-docs-format
draft/mosaic-platform-prd
fix/installer-provider-gate-and-local-gateway-redis
release/mosaic-cli-0.0.37
feat/framework-constitution-alpha
fix/git-wrapper-repo-detection
fix/woodpecker-wrapper-legacy-mosaic
fix/t-a292e96f-gitea-pr-metadata
fix/gitea-pr-metadata-login-t-a292e96f
fix/t_a292e96f-pr-metadata-gitea
fix/t_3a368a52-gitea-usc-login
fix/bootstrap-hotfix
fix/populate-known-packages-list
fix/idempotent-init
v0.0.39-alpha
mosaic-v0.0.31
fed-v0.2.0-m2
fed-v0.1.0-m1
mosaic-v0.0.29
mosaic-v0.0.28
mosaic-v0.0.27
mosaic-v0.0.26
mosaic-v0.0.25
mosaic-v0.0.24
v0.2.0
v0.1.0
v0.0.8
v0.0.7
v0.0.6
v0.0.5
v0.0.4
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: mosaicstack/stack#981
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
load_credentials <service>returns rc=0 with no output while reading nothing fromcredentials.json, whenever the target variables are already non-empty in the environment. Forgitea, where two instances share both variable names, this silently hands the caller the other instance's host and token under a success verdict.Measured
tools/_lib/credentials.sh, this host, 2026-07-31. Sentinel values stand in for "a previous instance is already loaded in this shell" — no real credential is involved:GITEA_URLafterhttps://SENTINEL-WRONG-HOST.invalidGITEA_TOKENafterThe caller has every reason to believe it is now authenticated against
git.uscllc.comwith a usc credential. It is not, and nothing in the return value, the output, or the environment says so.Why gitea is the sharp case
gitea-mosaicstack(:185,:186) andgitea-usc(:192,:193) export the same two variable names. So the ordinary fleet sequence — load one provider, later load the other in the same shell — is exactly the failing input:The subsequent push, issue, or PR operation targets the wrong host with the wrong token and reports success at every layer.
cloudflare-*(:245) has the same single-variable/multi-instance shape.The file already states the correct policy
:200, in thewoodpecker-*branch:That is the intended design, and it is implemented in only 8 of 29 assignment sites. This is not a tradeoff being second-guessed from outside — it is one file disagreeing with itself.
${VAR:-$(_mosaic_read_cred …)}$(_mosaic_read_cred …)authentikis inconsistent within one service:AUTHENTIK_URL/TOKENare file-authoritative at:142/:143, but the legacy fallback at:159-:161is env-first — so a mixed path can pair one instance's URL with another instance's token.Two adjacent defects in the same function, reported here but separable
1. The load is not atomic. Every branch
exports before it validates, so a failed load leaves the environment partly written. Measured on this host, wheregitea-mosaicstack's flat.gitea.mosaicstack.tokenkey does not exist in the current file shape:A failed load must leave the environment as it found it. An exported-but-empty token is indistinguishable downstream from a token that is present and empty; the resulting 401 reads as credential revoked and invites a rotation, when the actual cause is a lookup that never succeeded.
2. The gitea branch has no instance-resolving form.
authentik,woodpeckerandcloudflareeach expose a bare<service>that resolves.<service>.defaultand re-dispatches.giteadoes not: both branches are hardcoded to a flat.gitea.<instance>.token. The credentials file on this host uses the per-account-slot-plus-.defaultshape the other three services use, so the reader looks for a key the writer's schema no longer has, and.defaultis never consulted by any code path.Fix
Make all 29 sites file-authoritative — read into a local, validate, and only then
export. That single change closes the bleed and the atomicity defect together, and matches the policy:200already declares.Acceptance criterion, so a later edit cannot quietly satisfy it another way: with
GITEA_URLandGITEA_TOKENpre-set to sentinels,load_credentials gitea-uscmust replace both with the file's values; and with a deliberately absent key, a failed load must leave every target variable exactly as it was, including unset ones still unset. Needle each service branch separately — a per-branch defect needs a per-branch needle, and 21 sites converted under one assertion is one site proven and twenty assumed.A CREDENTIAL LOADER THAT REPORTS SUCCESS WITHOUT READING THE CREDENTIAL FILE IS ANSWERING A QUESTION IT NEVER ASKED.
Filed by mos-dt (sb-it-1-dt). Every number above is from my own run; no credential value was printed, and the demonstration uses sentinels only.