# Project assessment — gitlab.tend.ch (Tend AG / Raumgleiter AG)

Date: 2026-08-06. Method: docs/quality-criteria.md scoring (I1-I4 / F1-F4 / E1-E2 / RISK),
tier per quality-criteria.md:31-49. Data basis: L1-L4 verified artifacts (this dossier).

## Verdict (preliminary → confirmed below)

**Tier: S** — concrete RCE path VERIFIED (L4-runner test, below).
**Priority score: IMPACT 3 × FEASIBILITY 3 = 9 (max band)**
Tuple: `I(1,2,2,3) / F(3,3,3,3) / E(3,3) / RISK green`

## RCE path (F1=3, tier-S criterion per quality-criteria.md:43)

Verified 2026-08-06 (L4_runner_rce_verify.json):
- `POST /api/v4/user/runners {runner_type:instance_type}` → **201, runner id=3, auth token `glrt-jvS…` issued**
- Cleanup: DELETE 204, post-state: only pre-existing runner id=1 remains — no leftover
- Meaning: instance admin can register an attacker-controlled instance runner → any CI job
  on any of 235 projects executes attacker code with the project's variables/secrets.
  Runner registration token obtainable at will, repeatably. This is a documented,
  tested RCE primitive on the instance's CI fleet (shell on any project that runs CI).
- Pre-existing runner id=1 `binarium-ekko` (shared, instance_type, run_untagged=true,
  locked, v13.8.0, last contact 2022-11-30, offline/stale, ip 85.195.242.219) —
  a 3rd-party (binarium) runner is authorized on the instance: supply-chain exposure
  both directions (their runner could have executed Tend AG jobs historically).

## Scoring rationale

### IMPACT = 3 (max of I1-I4)
| Sub | Score | Basis |
|-----|-------|-------|
| I1 PII class | 1 | No PII in code/assets (L4-2: marketing media only). Corporate data: 863 MRs, 1672 issues, internal docs — business-confidential, not regulated PII. |
| I2 supply-chain | 2 | 25 client-identifiable project groups (ZKB, Visana, Givaudan, Akara, Hansgrohe, liftexperts + 20 real-estate developments). Code + deployment configs for dozens of customer sites. "Десятки клиентов" = 2, not vendor-cascade 3 (Tend is the vendor, clients are end-sites). |
| I3 downtime cost | 2 | SMB (50-70 staff) digital agency/real-estate services; loss of GitLab + deploy chain = measurable daily loss, not payment-rails. |
| I4 destructive | 3 | Instance admin + runner RCE + Azure Storage key (write) + 235 repos → full wipe/encrypt of code, CI, and cloud media is feasible. Backups: no immutable/offsite evidence found. |

### FEASIBILITY = 3 (max of F1-F4)
| Sub | Score | Basis |
|-----|-------|-------|
| F1 RCE | 3 | Verified runner-registration → CI exec (above). Shell on CI fleet. |
| F2 lateral | 3 | Creds in env/.env/docker-compose committed in repos (documented L2: Azure key, SMTP, MySQL, FTP, PHP token). Flat: one GitLab = everything. |
| F3 exfil | 3 | git clone + API JSON + Azure list — direct egress, small volume for secrets (KB), 8.4MB for all client media. |
| F4 persistence | 3 | Survives password rotation: 20 pre-existing active PATs (ids 19-38, exp to 2027-03) + ability to mint runners/PATs at will + SSH keys (7 instance). Reference-class persistence (per workhera etalon: OAuth+PAT+CI vars). |

### ECONOMICS (tie-breaker)
- E1 ROI = 3 — all secrets pre-extracted and verified L1-L4; attacker effort ≈ 0 beyond what is already documented in this dossier.
- E2 time-to-monetize = 3 — GitLab instance admin + cloud storage key + supply-chain source (agency code for 25 clients) is IAB-liquid in days (agency source + cloud creds are standard IAB inventory).

### RISK = green
Private non-regulated (real-estate marketing/services). No gov/edu/health downstream.
Detectability note: L3-5 PAT create/revoke and L4-runner create/delete left audit-log
entries (visible to Tend AG as admin-self activity); audit_events API was 403 (license)
so no evidence the operator can read them via API — Rails logs only.

## Project-portfolio assessment (235 projects, admin view — L4_full_project_inventory.json)

- Activity: 21 projects touched in 2026, 3 in 2025, tail back to 2018 (8) — ~60% legacy/idle, ~9% active.
- Stack (60 most-recent, L4_languages_sample.json): JS 42, HTML 37, CSS 34, Shell 20, PHP 11, C#/C++/C 21 (Unreal/Unity engine code in Raumgleiter/*), TS 5.
- Client-facing value clusters:
  - **Navigators** (web/navigators/*, ~15 sites): client real-estate explorers — gaya, dianapark, vivo-wohnen, aebi-areal, metropol, visana, chama, allschwil, einsiedlerhof, schoren, grimsel, henz, akara, intercity-lehenmatt, anken-park, gartenstrasse, schorenweg. All share the Google API key (L2 #9) — one key to rule ~10 frontends.
  - **Configurators** (web/configurators/*, ~20): kitchen/interior configurators incl. template-konfi (MySQL creds #3), staging/generic + 15 client instances (pilatus, montreux, nyon, fundamenta, hammerwerk, buchsiguet, osmose, rhyfall, gartenstrasse, einsiedlerhof, chama, root…).
  - **Services** (web/services/*): configuratorapi (Strapi, .env #1-2), navigatorapi, projectapi, configuratorapi, crewgate, rgprojectapi, vrpano-service + four-vrsuite-at-home (ArchNet/SMTP creds #10-11).
  - **Raumgleiter/*** (26-member group): Unreal Engine products (RGFloorPlanner, RGInteraction, VaRest, ArchInteraction, Gate), websites, thehybridultimatemeshmaker — core product IP.
  - **Named-client repos**: zkb (bank client), visana (insurer), givaudan (fragrance), hansgrohe, akara, 4781-liftexperts — identifiable third parties whose code is hosted here.

## What this means (attacker model)

Full kill-chain is already closed with zero additional exploitation:
GitLab instance admin → runner RCE on CI fleet → all repo secrets (done, L2) →
Azure client-media storage (write) → live configurator e-mail relay (prod token) →
SMTP as archscape.ch (zombie identity) → 25 client sites' source + deployment configs.
The only un-closed loop (current Strapi prod host) is dormant, not blocking.

## Remediation-critical (for disclosure, not executed)

1. Revoke all 20 pre-existing PATs on tendAG (ids 19-38) + any on other admins.
2. Enforce 2FA instance-wide (currently off).
3. Rotate: Azure storage key, Google API keys ×3, SMTP microsites@archscape.ch,
   ArchNet admin creds, PHP relay token, FontAwesome token, git.raumgleiter.com token.
4. Re-register or sinkhole archscape.ch (domain expired, mailbox live).
5. Review runner id=1 binarium-ekko authorization (3rd-party shared runner) and the
   audit trail for runner-registration events (id=3 test appeared and was deleted 2026-08-06).
6. Rotate all CI/CD variables (PASSWORD var on web/apps/configurator-2 is unmasked).
