SECURITY: one tea logins table interleaves both estates — a wrapper pointed at the wrong host succeeds AS THE OTHER ESTATE (host-string matching is the only boundary) #1049

Open
opened 2026-08-05 14:45:51 +00:00 by Mos · 0 comments
Contributor

The estate boundary is currently enforced by string matching in a shared table

A single tea logins table interleaves BOTH estates on the same host. Observed live — one table holding, side by side:

  • USC (git.uscllc.com): usc, usc-mos, be-coder-03, be-coder-04, gate-ultron-01, coder2, coder3, rev1, rev-974, rev-3107b, ultron-3107, f10-coder, uc-lead
  • HOMELAB (git.mosaicstack.dev): mosaicstack-mos, mosaicstack-rev-974, tl-mosaic

get_gitea_login_for_host discriminates on host string and nothing else. There is no estate field, no namespace, and no guard.

The failure mode

A wrapper pointed at the wrong host does not fail — it SUCCEEDS AS THE OTHER ESTATE.

That is the same fail-silent shape as the shared-credential fallback in #1044, one layer up: not a refusal, a successful action under the wrong principal — here under the wrong estate's principal. And it composes badly with the known first-host-match behaviour: a login that fails to resolve falls back to whatever sorts first for that host, so a near-miss lands on a real, working identity rather than erroring.

Note also two same-name identities already exist across estates (rev-974 and mosaicstack-rev-974 are distinguished only by a naming convention that nothing enforces). A convention is not a control.

Why this matters now

Cross-estate work is increasing, not decreasing: agents on hosts in one estate routinely target repositories in the other, so "which estate am I acting in" is no longer implied by where the process runs. The operator has made estate declaration mandatory in agent communications for exactly this reason; the tooling has no corresponding enforcement.

Ask

  1. Namespace or tag logins by estate so selection cannot silently cross — e.g. an explicit estate field consulted alongside host, or enforced per-estate login prefixes validated at add-time.
  2. Fail closed on ambiguity: if the requested identity does not resolve within the expected estate, refuse — do not fall back to a host-adjacent match.
  3. Consider separate credential stores/config per estate rather than one interleaved table, so a misconfiguration cannot reach across.

Not proposing a mid-mission fix — filing so the boundary stops depending on string matching.

Related caveat on this issue's own provenance

The behaviours described here (first-host-match resolution, the permission gradient, wrapper gaps) were measured on one estate's Gitea. The code is shared, so the defect class is general — but the observed instances (which login a fallback lands on, which permissions each seat holds) are per-estate facts and are unverified on the other side. Anyone acting on this should re-measure in their own estate rather than assume the instance transfers. Same caveat applies to the measurements in #1043, #1044, #1046 and #1047.

## The estate boundary is currently enforced by string matching in a shared table **A single `tea logins` table interleaves BOTH estates on the same host.** Observed live — one table holding, side by side: - **USC** (`git.uscllc.com`): `usc`, `usc-mos`, `be-coder-03`, `be-coder-04`, `gate-ultron-01`, `coder2`, `coder3`, `rev1`, `rev-974`, `rev-3107b`, `ultron-3107`, `f10-coder`, `uc-lead` - **HOMELAB** (`git.mosaicstack.dev`): `mosaicstack-mos`, `mosaicstack-rev-974`, `tl-mosaic` `get_gitea_login_for_host` discriminates on **host string and nothing else**. There is **no estate field, no namespace, and no guard.** ### The failure mode **A wrapper pointed at the wrong host does not fail — it SUCCEEDS AS THE OTHER ESTATE.** That is the same fail-silent shape as the shared-credential fallback in #1044, one layer up: not a refusal, a successful action under the wrong principal — here under the wrong *estate's* principal. And it composes badly with the known first-host-match behaviour: a login that fails to resolve falls back to whatever sorts first for that host, so a near-miss lands on a real, working identity rather than erroring. Note also two same-name identities already exist across estates (`rev-974` and `mosaicstack-rev-974` are distinguished only by a naming convention that nothing enforces). A convention is not a control. ### Why this matters now Cross-estate work is increasing, not decreasing: agents on hosts in one estate routinely target repositories in the other, so "which estate am I acting in" is no longer implied by where the process runs. The operator has made estate declaration mandatory in agent communications for exactly this reason; **the tooling has no corresponding enforcement.** ### Ask 1. **Namespace or tag logins by estate** so selection cannot silently cross — e.g. an explicit estate field consulted alongside host, or enforced per-estate login prefixes validated at add-time. 2. **Fail closed on ambiguity**: if the requested identity does not resolve *within the expected estate*, refuse — do not fall back to a host-adjacent match. 3. **Consider separate credential stores/config per estate** rather than one interleaved table, so a misconfiguration cannot reach across. Not proposing a mid-mission fix — filing so the boundary stops depending on string matching. ### Related caveat on this issue's own provenance The behaviours described here (first-host-match resolution, the permission gradient, wrapper gaps) were **measured on one estate's Gitea**. The *code* is shared, so the defect class is general — but the *observed instances* (which login a fallback lands on, which permissions each seat holds) are per-estate facts and are **unverified on the other side**. Anyone acting on this should re-measure in their own estate rather than assume the instance transfers. Same caveat applies to the measurements in #1043, #1044, #1046 and #1047.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#1049