Skip to content

Security

Scope

Threat model + mitigations for worker-kmp v3+. Per RULE-SECRETS-VAULT-001 + the worker-kmp v3.0.0 epic Phase 10 (security threat model). Internal STRIDE audit: 2026-05-27.


Reporting a vulnerability

Use GitHub Security Advisories for private disclosure

Open a private advisory → We respond within 7 days. Please do not file public issues for security vulnerabilities.


STRIDE matrix

The following 28-row STRIDE table covers every attack surface introduced by worker-kmp v3.0.0. Each row is either MITIGATED (with code reference) or ACCEPTED (with explicit rationale referencing security-assumptions.md).

Desktop daemon JAR (Phase 8)

ID Surface Threat class Status Mitigation / Rationale
T1 daemon JAR Spoofing (unsigned JAR substitution) TBD Phase 8 T4-T5: SHA-256 integrity check at startup; mismatch → exit non-zero. Defends against post-install substitution.
T2 daemon persistence files Tampering (other-process file edits) TBD Phase 8 T7-T8: HMAC-SHA256 per .properties file using per-install ephemeral key (mode 600). Daemon refuses invalid HMAC.
T3 daemon logs Repudiation (log destruction) TBD Phase 10 T32-T34: log dir mode 700; rotating 1MB × 3; security events go to sticky security.log (never rotated/deleted).
T4 daemon stack traces Info disclosure (path leaks) TBD Phase 10 T32-T34: log entries strip input/output by default (<redacted, N bytes>); opt-in --verbose-log.
T5 daemon lock file DoS (lock removal → multi-daemon) TBD Phase 8 T9: FileChannel.lock + PID; stale-lock detection compares PID to running processes.
T6 daemon process privilege Elevation (runs as consumer-app user) ACCEPTED Documented in security-assumptions.md §"Daemon privilege boundary". Alternative is system service requiring sudo at install (out of scope per Phase 8). Risks bounded to user's own data.

Web Push subscription endpoint (Phase 9)

ID Surface Threat class Status Mitigation / Rationale
T7 endpoint URL Spoofing (URL guessed) ACCEPTED RFC 8030 + VAPID key (only key holder can send pushes anyway). Defense-in-depth via T14's server obligations.
T8 subscription Tampering (replay over long horizons) TBD Phase 9 T13: subscriptionExpiryDays default 90; subscriptions auto-refresh.
T9 server logs Repudiation (log disclosure) TBD Phase 10 T11-T12: PushSubscriptionLogger — never logs raw endpoint URL; hashes endpoint to sha256:first8chars.
T10 push endpoint = device identifier Info disclosure: PII TBD Phase 10 T11-T15: endpoint hashed in logs; server-side: encrypt subscription rows at rest; rate-limit /push/subscribe.
T11 subscription table DoS (unbounded growth) TBD Phase 9 T13: 90-day auto-refresh + server rate-limit (T14).
T12 server compromise Elevation (push storm) TBD Document server hardening in ../features/web-push-server.md (T14); VAPID key rotation annually.

Service Worker push handler (Phase 9)

ID Surface Threat class Status Mitigation / Rationale
T13 SW script source Spoofing (compromised CDN) TBD Phase 10 T18: WebPushConfig.serviceWorkerIntegrityHash + Integrity-Match server header; refuses if mismatch.
T14 IndexedDB Tampering by tab JS TBD Phase 10 T20: WebWorkPersistence schema-validates each entry; deserialize throws IntegrityException on tamper.
T15 SW push event log Repudiation (push logged with PII) TBD Phase 10 T11-T12: PushSubscriptionLogger applies in SW context too; redacts payloads.
T16 push payload → DOM Info disclosure (payload echoes) TBD Phase 10 T17-T19: ESLint config forbids document.write / innerHTML / eval in worker-kmp-sw.js.
T17 SW kills self DoS (bad payload) TBD Phase 10 T19: SW handler strictly validates payload.type === 'WORKER_KMP_TRIGGER' && typeof payload.scope === 'string'; anything else dropped silently.
T18 SW imports Elevation (compromised import) TBD Phase 10 T17: SW carries // CSP: no eval, no document.write, no innerHTML top-comment + ESLint enforces.

VAPID key lifecycle (Phase 9)

ID Surface Threat class Status Mitigation / Rationale
T19 VAPID private key Tampering (committed to source) TBD Phase 10 T21: /secrets push --generate vapid integrates with framework vault per RULE-SECRETS-VAULT-001. Key NEVER echoed; stored in SOPS+age-encrypted vault.
T20 VAPID private key on disk Info disclosure TBD Phase 10 T22: ../features/web-push-server.md mandates: never commit / email / paste in chat. Annual rotation.
T21 leaked VAPID key Elevation (push to all subscribers) TBD Phase 10 T22: rotation procedure documented. /secrets rotate vapid_private_key immediate response.

Koin factory injection (Phase 0)

ID Surface Threat class Status Mitigation / Rationale
T22 WorkerRegistry Spoofing (fake worker registered) TBD Phase 10 T30: workKoinModule rejects worker class names containing .., /, or null bytes.
T23 WorkerRegistry post-start Tampering TBD Phase 10 T29: registry becomes immutable after workKoinModule loads into Koin; subsequent register() throws WorkerRegistryAlreadyLoadedException.
T24 registered worker Elevation (more privileges than expected) ACCEPTED Workers run in consumer-app process at consumer-app privilege. Documented in security-assumptions.md §"Worker privilege boundary".

Notifications permission (Phase 7)

ID Surface Threat class Status Mitigation / Rationale
T25 Notification.requestPermission Spoofing (perma-grant abused) TBD Phase 10 T24-T25: requireNotificationPermission requires consumer-side explicit opt-in via WebPushConfig.notificationPermissionAutoRequest = true OR explicit subscriber.requestPushPermission() call. Library never auto-prompts.
T26 notification body Info disclosure (work content) TBD Phase 10 T26-T27: ForegroundInfo.Builder.setContentSafe(title, message) sanitizes strings; raw setter @Internal. ../features/foreground-tasks.md warns about lock-screen visibility.
T27 permission deny DoS (user-permanent-deny) ACCEPTED UX is consumer responsibility. PRIVACY.md documents soft-ask pattern.

Foreground service notification (Phase 1)

ID Surface Threat class Status Mitigation / Rationale
T28 foreground notification Info disclosure (lock-screen visibility) TBD Phase 10 T27: ../features/foreground-tasks.md warns consumers about sensitive content; recommends Notification.VISIBILITY_PRIVATE.

Auditing dependencies

  • OWASP dependency-check runs on every PR (.github/workflows/security-scan.yml)
  • npm audit on samples/web-push-server-node/
  • gitleaks on every push
  • SBOM (CycloneDX) generated per release; attached to GitHub Release

Audit history

Date Audit type Outcome
2026-05-27 Internal STRIDE (Phase 10 of worker-kmp v3.0.0 epic) 28-row matrix initial draft; mitigations TBD as Phases 0/7/8/9 land

See also

  • security-assumptions.md — trust assumptions list
  • threat-model-template.md — consumer-extension template
  • Framework rule: RULE-SECRETS-VAULT-001
  • Phase 10 sub-plan: plan-layer/project-plans/mbs/worker-kmp/active/worker-kmp-v3-foreground-storeflow/10-security-threat-model.md