Files
familienarchiv/.specify/features/_example/threat-model.md
Marcel 01f51854f6 feat(sdd): add .specify scaffold — constitution, AGENTS, personas, templates, example, RTM
Introduces the SDD root: a v1.0.0 constitution and machine-readable AGENTS.md
grounded in the project's real conventions; six EARS-aware persona spec-review
checklists that cross-reference .claude/personas/; feature-spec/ADR/threat-model/
api-contract templates; a fully worked _example feature; a living RTM; and an
adrs/ pointer that reuses the existing docs/adr/ archive.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 11:56:31 +02:00

2.8 KiB
Raw Blame History

Threat Model — Profile picture upload

Feature spec: ./spec.md Date: 2026-06-13 Author: Security persona (worked example)

Data Flow Diagram (text)

Actors

  • Anonymous visitor (unauthenticated)
  • Authenticated user (uploads their own avatar)
  • Admin (Permission.ADMIN_USER — may remove others' avatars)

Trust boundaries

  • TB-1: Browser ⇄ Caddy (public internet ⇄ DMZ)
  • TB-2: Caddy ⇄ Backend :8080 (DMZ ⇄ app)
  • TB-3: Backend ⇄ MinIO + PostgreSQL (app ⇄ data plane)

Data flows

  • F-1: Browser → [TB-1,TB-2] → UserAvatarController : multipart image
  • F-2: UserService → [TB-3] → MinIO : object at avatars/{userId}
  • F-3: UserService → [TB-3] → PostgreSQL : app_users.avatar_object_key
  • F-4: Browser → [TB-1,TB-2,TB-3] → MinIO (via proxy GET) : image bytes

STRIDE

Threat Category Asset / Flow Threat Description Mitigation Likelihood × Impact Status
Spoofing F-1 Unauthenticated caller uploads/deletes an avatar Session auth required; @RequirePermission (REQ-006) Low × Med Mitigated
Tampering F-3 Caller sets avatarObjectKey via request body to point at an arbitrary stored object avatarObjectKey is server-set in UserService only, never bound from body (CWE-639) Med × High Mitigated
Repudiation F-2/F-3 No record of who changed an avatar Standard request logging by user UUID (no PII); admin deletions auditable via existing logs Low × Low Accepted
Information disclosure F-4 A public/signed S3 URL would let anyone fetch any avatar without auth Avatars served only through the authenticated proxy GET /api/users/{id}/avatar; no public URL Med × Med Mitigated
Information disclosure F-1 Malicious file (polyglot) served back with a sniffed content type → stored XSS Store with a fixed image/png/image/jpeg content type; proxy sets Content-Type + X-Content-Type-Options: nosniff; only PNG/JPEG accepted (REQ-007) Low × High Mitigated
Denial of service F-1/F-2 Oversized or many uploads exhaust storage/memory 2 MB cap enforced before MinIO write + multipart.max-file-size ceiling (REQ-008); deterministic key means one object per user Med × Med Mitigated
Elevation of privilege F-1 Non-admin removes/replaces another user's avatar via /{id} Ownership check; ADMIN_USER required for /{id} (REQ-005/REQ-009, 403) Low × Med Mitigated

ASTRIDE

Not applicable — this feature invokes no AI agent, model, or tool.

Residual Risk

  • Repudiation (Accepted): avatar changes are not written to a dedicated audit table. Accepted because the asset is low-value (a self-chosen picture) and request logs already attribute the action to a user UUID. Revisit if avatars ever become trust signals.