# V4 Workshop / GameFeature-Plugin UGC Threat Model

Status: Active. Created 2026-06-12 to close the audit gap
(`V1_V7_PLAN_SET_AUDIT_2026-06-12.md` §6.2, V4): the workshop spec asserts
that "a mod that crashes is caught, auto-disabled, and reported"
(`V4/V4_features.md:1728-1731`) — a **reliability** claim — and calls mods
"sandboxed GameFeatures plugins" (`:1710-1711`) without defining the sandbox.
This document is the security analysis. Its trust-tier decision (§3) and GA
gates (§7) are binding inputs to the workshop launch exit criterion
("Workshop / mod platform live", `V4/V4_features.md:2751`) and to risk R-08 in
`V4/docs/planning/program-risk-register.md`.

## 1. What a GameFeature plugin actually is (the gap in the current claim)

A UE GameFeature plugin can contain: cooked assets (meshes, textures, audio,
data tables), Blueprint classes (bytecode executed in-process with access to
every `BlueprintCallable` engine API), GameFeature actions (component/ability
injection into live actors), and — for first-party plugins — native C++
modules. Nothing in the GameFeature subsystem is a security boundary: a
Blueprint runs with the full privileges of the game process. Crash-catching
(`V4/V4_features.md:1728-1731`) protects against *bugs*; it does nothing
against *malice*, and "isolated plugin" describes packaging, not privilege.

Adjacent specced surfaces this model must cover: the mission editor's
"sandboxed" visual script ("scripts cannot call engine internals or arbitrary
code", `V4/V4_features.md:1657-1659`), the mod manifest/dependency closure
(`:1716-1719`), immutable versioning (`:1732-1734`), two-stage content
moderation + takedown cascade (`:1723-1727`), Steam Workshop + EOS Player
Studio distribution (`:1713-1715`), and the esports mod whitelist
(`:1896-1898`).

## 2. Threat enumeration

| ID | Threat | Vector | Worst case | Status under current spec |
| --- | --- | --- | --- | --- |
| T1 | **Arbitrary native code in plugins** | Mod ships a compiled module; GameFeature loader maps it into the game process | Full RCE on every subscriber's machine; ransomware/credential theft at workshop scale; console TRC catastrophe | Not explicitly forbidden anywhere — must be forbidden (§3) |
| T2 | **Blueprint logic mods as de-facto arbitrary code** | BP bytecode calls exposed engine APIs: file I/O via save-game/paths nodes, console-command execution, process-level side effects via plugins' loaded classes | Equivalent to T1 in practice (data theft, cheat injection, griefware), while looking like "just a Blueprint" | "Sandboxed" asserted, no mechanism specced — resolved by §3 tiers |
| T3 | **UObject / asset deserialization attacks** | Malicious `.uasset` exploits parser/serializer bugs (UE asset loaders have a history of memory-safety CVEs); triggers on *load*, before any "logic" runs | RCE from an "asset-only" mod; defeats any logic-tier policy if unmitigated | Unaddressed — mitigated by re-cook pipeline + loader fuzzing (§4) |
| T4 | **Cheat distribution via cosmetic mods** | Materials with depth-test disabled (wallhack), high-visibility player skins, foliage/smoke-removing meshes, hitbox-revealing particles, tampered data tables | Competitive integrity collapse; EAC (ADR 0008) does not inspect workshop asset semantics | Partially addressed: esports whitelist exists (`:1896-1898`); ranked needs the same guarantee (§5) |
| T5 | **Supply-chain via popular-mod update** | Author account compromise → malicious vN+1 offered to the full subscriber base; immutable versions (`:1732-1734`) protect pins but the update *offer* path is the attack | One popular mod = distribution to 10⁵–10⁶ clients in hours | Unaddressed — author 2FA, rescans per version, staged rollout, kill switch (§4, §6) |
| T6 | **Dependency-closure attacks** | Typosquatted or hijacked dependency in the manifest closure (`:1716-1719`); client "resolves and downloads the dependency closure" automatically | Same blast radius as T5, harder to see | Unaddressed — closure pinned by content hash at publish (§4) |
| T7 | **Asset-bomb denial of service** | Pathological Nanite meshes, 8K texture stacks, runaway Niagara emitters, audio loudness bombs | OOM/hang on constrained platforms (Switch 2, tablets — `V4/V4_features.md:300-304`); review-bombing-by-crash | Partially: save quotas exist (`:1667-1669`); needs budget linting at upload (§4) |
| T8 | **Data exfiltration / phishing surfaces** | Web/URL-capable widgets or nodes in logic mods; malicious links in workshop descriptions | Credential phishing branded inside the game | Moderation queue helps descriptions; logic-tier API allowlist removes in-game vector (§3) |
| T9 | **Moderation-evasion content** | Classifier-evading textures/meshes (the two-stage gate at `:1723-1725` is image-classifier + reports) | Brand/legal damage, store delisting | Accepted residual; takedown cascade + §6 revocation bounds exposure time |
| T10 | **Platform-holder/cert violation** | Any downloaded executable logic on consoles violates TRC/XR content policies | Cert rejection of the *base game* update carrying the workshop client (risk R-02/R-08) | Resolved structurally by §3: consoles receive Tier-0/Tier-1 only |

## 3. Trust-tier design — the explicit call

**Decision (binding, planning decision adopted 2026-06-12, to be ratified as
an ADR if contested): V4 does NOT distribute arbitrary native code or
arbitrary Blueprint GameFeature plugins to client machines. Ever.** The
workshop's "GameFeatures plugin" packaging is retained, but what a plugin may
*contain* is tiered:

| Tier | Contents | Where it runs | Distribution |
| --- | --- | --- | --- |
| **Tier 0 — Asset-only** | Cooked assets + data tables conforming to published schemas; **zero bytecode of any kind** | All platforms, incl. consoles via EOS Player Studio | After §4 pipeline (re-cook, scans, signing). "Unrestricted" in *availability*, never in *validation* |
| **Tier 1 — Interpreted logic** | Mission-editor visual scripts (`V4/V4_features.md:1657-1659`) compiled to a **restricted interpreted instruction set** executed by a V4-owned interpreter: opcode allowlist, capability-scoped API (spawn/objective/dialogue/trigger verbs only), per-tick instruction budget, no file/net/console access | All platforms; PvE and custom games only | Same pipeline + interpreter-validation pass |
| **Tier 2 — Server-side Blueprint plugins** | Full Blueprint GameFeature plugins (new modes, mutators, AI behaviors) | **Dedicated servers only** — our fleet's custom-game pools and self-hosted unranked servers. Clients receive only replicated state plus the mod's Tier-0 asset payload; the logic plugin never ships to a client process | PC server hosts + first-party fleet; never consoles, never client installs |
| **Tier 3 — Native plugins** | C++ modules | First-party and contracted-partner code only, through the normal build/signing pipeline, code-reviewed | **Not workshop-distributable.** No exceptions; a partner native mod is a game patch, not a mod |

Consequences of the call:

- T1 is closed by fiat (Tier 3 not workshop-distributable); T2 is closed by
  construction (Blueprint logic exists only behind the server boundary,
  Tier 2); T10 is closed structurally (consoles get no executable content).
- The mission editor's existing sandbox claim becomes *true by mechanism*:
  Tier 1 is an interpreter we own, not trust in Blueprint.
- Cost accepted: client-side total-conversion mods (new client logic) are out
  of scope for V4. That is the price of 9-platform cert and EAC coexistence.

### Position relative to V5 and V7

- **V5 (data-only):** V5's workshop is "data-only, cannot ship executable
  code, and is disabled in ranked PvP and on cert-restricted online surfaces"
  (`V5/V5_features.md:2315-2318`). V4 Tier 0 matches V5's line; V4 goes
  further than V5 by adding Tier 1 (interpreted mission logic) and Tier 2
  (server-side Blueprint) because V4's creator suite promises per-cell
  *mission logic*, which data alone cannot express.
- **V7 (WASM capability sandbox):** V7 lifts the data-only limit with a
  Wasmtime Component-Model sandbox — capability-typed WIT host interface,
  fuel/epoch CPU budgets, `StoreLimits` memory caps, six trust tiers, AOT at
  upload, per-call audit log (`V7/V7_ARCHITECTURE.md:338-369`), with
  content-addressed storage + lock files as the supply-chain primitive
  (`:371-384`). V4 deliberately stops short of client-side untrusted
  execution because it has no such runtime: UE Blueprint offers no capability
  typing, no deterministic fuel metering, and no memory isolation. **V4's
  line: interpretation (Tier 1) + server isolation (Tier 2) substitute for
  the sandbox V4 doesn't have.** Forward-compatibility requirement: the V4
  mod manifest carries a `trustTier` field whose values map 1:1 onto V7's
  DataOnly/Scripted/Extended tiers (`V7/V7_ARCHITECTURE.md:355-358`) so V4
  workshop content can migrate when a WASM-class runtime exists; V4 adopts
  V7's content-addressing model for closure pinning now (§4) since it is
  runtime-independent.

## 4. Signing and scanning pipeline (upload → activation)

1. **Manifest validation** — declared cells, asset list, dependencies, semver,
   author identity (`V4/V4_features.md:1716-1719`); `trustTier` declared and
   verified against contents (a Tier-0 mod containing any bytecode is
   rejected, not downgraded).
2. **Bytecode & class audit** — package is statically walked: Tier 0 must
   contain zero Blueprint bytecode and only allowlisted UObject classes;
   Tier 1 scripts compile through the first-party toolchain server-side (the
   uploaded source graph, not author-supplied bytecode, is authoritative);
   Tier 2 Blueprint passes an API-usage scan (deny-listed nodes: console
   command exec, file path access outside mod sandbox dir, process/OS nodes).
3. **Re-cook / re-serialization on trusted infrastructure** — every asset is
   deserialized and re-serialized by our cooker; unknown or malformed
   structures are dropped, closing the T3 window to the cooker (which we fuzz,
   §7 G1) rather than every player's client. Clients only ever load
   first-party-cooked bytes.
4. **Semantic asset scans** — material flags (depth-test disable, unusual
   translucency sort priorities), mesh/texture/emitter budget linting against
   per-platform caps (T7), audio loudness normalization, data-table values
   range-checked against schema bounds (T4 partial).
5. **Content moderation** — the specced classifier + report queue
   (`V4/V4_features.md:1723-1727`) runs after technical scans.
6. **Content-addressed signing** — artifact ID = hash(content + full
   dependency closure) (V7 model, `V7/V7_ARCHITECTURE.md:379-384`); the
   workshop service signs {artifact hash, manifest, trustTier, version}.
   Clients/servers verify signature and the **pinned closure hashes** before
   activation — a swapped dependency changes the hash (closes T6).
7. **Per-version re-scan** — every update version repeats 1–6 (versions are
   immutable, `:1732-1734`); update **offers** to subscribers roll out staged
   (1% / 24 h → 25% / 24 h → 100%) with automatic halt on crash/report
   anomaly (T5).
8. **Author account controls** — publishing requires 2FA; a publish from a
   new device/IP triggers step-up verification; author keys never sign
   anything (only the service signs), so author compromise cannot mint
   trusted artifacts directly.

## 5. Competitive-surface guarantees (T4)

- **Ranked and tournament processes load zero workshop content** — enforced
  by a build-time assertion in the mode-activation path, the same pattern as
  the AI-Director PvP exclusion assertion (`V4/V4_features.md:1561-1564`),
  not by playlist configuration. Tournament servers already reject
  non-whitelisted clients (`:1896-1898`); ranked inherits the stricter rule:
  no mods, whitelisted or not.
- Custom/unranked lobbies display the active mod set to all players before
  ready-up; server-side hit validation and EAC remain active regardless.

## 6. Kill switch and revocation

- **Revocation list (CRL-model):** workshop service publishes a signed
  revocation feed; clients fetch at session start and on the existing
  30-minute hotfix channel (`V4/V4_features.md:1961-1962`) for emergency
  push. Cached CRL is valid offline for 72 h; beyond that, Tier 1/2 content
  deactivates until the client can revalidate (Tier 0 remains, lower risk —
  planning assumption adopted 2026-06-12, revisit after Open Beta data).
- **Revocation classes:** `security` (T1–T6 findings — immediate hard-disable,
  subscribers notified, telemetry snapshot of activation counts preserved for
  incident response), `integrity` (T4 cheat assets — disable + ranked-history
  audit hook for affected lobbies), `moderation` (existing takedown cascade,
  `:1723-1727`, including dependent-mod delisting).
- **Drill requirement:** end-to-end revocation (decision → fleet + clients)
  must complete in ≤15 minutes in staging, exercised quarterly and once
  before GA (§7 G4).

## 7. Gates this adds before Workshop GA

Workshop GA is an exit criterion (`V4/V4_features.md:2751`). It does not ship
until all of:

- **G1 — Cooker hardening:** re-cook pipeline operational for all Tier-0 asset
  classes; ≥500 CPU-hours of coverage-guided fuzzing on the deserialization
  path with zero outstanding crash/UB findings (planning assumption adopted
  2026-06-12 for the fuzz budget); corpus retained and run in CI.
- **G2 — Competitive isolation proven:** the §5 build-time assertion exists
  and a red-team attempt to load a mod into a ranked process fails in CI.
- **G3 — Signing chain live:** content-addressed signing + closure pinning
  verified on all 9 platforms; author 2FA enforced; staged-rollout machinery
  tested with a forced halt.
- **G4 — Kill-switch drill:** ≤15-minute end-to-end revocation demonstrated in
  staging, all three revocation classes.
- **G5 — Platform-holder signoff:** written approval of the per-platform UGC
  scope (Tier 0/1 on consoles, no executable content) from each console
  holder — due 2026-07-15 per risk R-08's trigger.
- **G6 — Service pen test:** third-party penetration test of workshop-service
  upload/entitlement/CDN paths with no open high/critical findings.
- **G7 — Interpreter bounty window:** Tier-1 interpreter in the security
  bug-bounty scope for ≥30 days before GA (Open Beta window suffices if the
  interpreter ships in Open Beta), with sandbox-escape classed as critical.

## 8. Residual risks (accepted, with eyes open)

- T3 residual: a cooker-fuzzing gap could still let a crafted asset through —
  bounded by trusted re-cook (attacker must exploit *our* cooker output
  format, not hand-craft bytes) and the 15-minute kill switch.
- T4 residual: semantic scans cannot catch every advantage-granting cosmetic;
  ranked-zero-mods contains the competitive harm; custom games accept it
  visibly.
- T9 residual: classifier evasion until report-queue action; bounded by
  takedown cascade SLA (set: 4 h for confirmed security/integrity, 24 h
  moderation — planning assumption adopted 2026-06-12).
- Scope cost: no client-side logic total conversions in V4 (revisit when a
  V7-class WASM runtime is adoptable in UE).

Owners: Security Lead (Trust & Safety) — document of record; Workshop Service
Lead — pipeline (§4); Online/Build leads — §5 assertion; Live Ops Director —
§6 drills; Platform & Certification Manager — G5.
