nigig-org/crates/apps/nigig-traffic/docs/THREAT_MODEL.md

3.6 KiB

nigig-traffic threat model (TRAFFIC-13, scoped)

Traffic is an OFFLINE, non-authenticated app. There is deliberately NO network, payment, account, telemetry, or instructor-sharing boundary in this tranche — and no such claims may appear in copy (ADR 001). Do not add web-style encryption/authentication work here. Real surfaces:

1. Dependency / toolchain supply chain

  • Pinned git dependency (andodeki/makepad fork) + crates.io packages.
  • Mutable CI bootstrap (actions/checkout@v4, rustup installer download).
  • Gates: Cargo.lock committed + cargo metadata --locked clean-tree check; advisory/license/duplicate/yanked/source policy (CI supply-chain job); third-party actions pinned to immutable SHAs; toolchain from controlled image or digest-verified installer; SBOM + license/source manifest archived per release (docs/SBOM.md at release).
  • Exceptions (advisory waivers, unlicensed crates) require owner, reason, and expiry, and fail closed when expired.

2. Scenario / profile / replay / asset parsing

  • Inputs: static catalog (code), future imported scenario packs, learner profile JSON, replay digests, GLB/image assets.
  • Policy: byte/count/depth/string/path limits BEFORE allocation/parse; reject absolute paths, .. traversal, backslashes, unsupported file types; bounded, allocation-free-first parsers (persistence.rs, assets.rs); fuzz the byte-accepting parsers/converters with a corpus (cargo fuzz target or #[test] hostile-archive suite at release).
  • Oversize/decompression-bomb/corrupt inputs fail within memory/time budgets without panic (no unwrap/expect in src/; CI greps it).

3. Path and resource exhaustion

  • Asset IDs are logical (sign_stop.glb), never filesystem paths; one validated join root; no CARGO_MANIFEST_DIR probes in packaged builds.
  • Budgets (§6) enforced by validators: ≤256 entities/scenario, ≤32 movers, ≤2048 statics, ≤64 criteria, ≤4096 rule events (ring cap), 30-minute attempt cap, 8 MiB profile, 10k attempts before compaction, 150 MiB curated assets, texture residency caps. Over-budget content is excluded from the catalog, never silently downscaled for assessment.

4. Local learner privacy

  • Anonymous by default: opaque learner ID (anon-*), no names, no telemetry, no cloud sync, no home-grown at-rest encryption.
  • Restrictive platform file permissions on the profile dir; atomic temp + flush/sync + rename writes; verify-by-reopen; recovery states (Absent / Corrupt-preserved / FutureVersion-read-only / IoError) that never silently reset to empty.
  • Logs are bounded and contain no profile contents or absolute developer paths. Export/delete/reset controls ship with persistence before any "saved locally" copy appears (settings copy is session-only until then).
  • Explicit NON-GOALS this tranche: accounts, instructor sharing, cloud backup, analytics, secondary use. Each needs a separately reviewed purpose, retention policy, consent flow, and threat model.

5. Unsafe / dependency-code scope

  • Source-level unsafe grep covers Traffic src/ only and does NOT constrain unsafe in dependencies (including the Makepad fork). Dependency risk is managed via pin + audit + SBOM, not grep.

Residual risks (accepted, tracked)

  • GUI harness cannot boot in CI (TRAFFIC-14): runtime regressions rely on headless production-path journeys until the harness is repaired.
  • No qualified curriculum review (TRAFFIC-08): all content is practice preview; assessment release is blocked.
  • Pinned fork is internally inconsistent (TRAFFIC-P0-01): builds fail closed until a coherent upstream revision is pinned.