# Risk map — immich-app/immich

> Canonical page: https://codevetter.com/unpack/immich/risk-map

# Risk map — immich-app/immich

Scanned commit d6d094738366aaf4d2fcefdfe2702f50c72ba5b0 (2026-09-22). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

Security-sensitive surface concentrates on file upload/disk IO, auth token handling, subprocess media tooling, and the WASM plugin host. The multer storage engine writes user-supplied bytes to predictable uuid paths, ffmpeg/exiftool process untrusted media, and extism plugins run third-party WASM gated by env vars. Operational risks include a 24h request timeout, trust-proxy assumptions, startup-time migrations under an advisory lock, and a supervisor that kills the whole process on worker failure. Several safety rails exist (mandatory @Authenticated, duplicate-job startup validation, constant-time login compare) but are themselves fragile coupling points.
- File upload writes untrusted bytes through a custom multer _handleFile to disk paths derived from StorageCore folders; filenames are sanitized via sanitize-filename and uuid-based, but the interceptor runs before controller validation, so malformed multipart payloads hit the FS layer early. — [server/src/middleware/file-upload.interceptor.ts#L48-80](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/middleware/file-upload.interceptor.ts#L48-L80) [server/src/services/asset-media.service.ts#L91-115](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/services/asset-media.service.ts#L91-L115)
- express 'trust proxy' is set to ['loopback', ...network.trustedProxies] from IMMICH_TRUSTED_PROXIES; clientIp used in login logging/GetLoginDetails depends on this being correct — misconfiguration enables IP spoofing in audit logs and rate-sensitive paths. — [server/src/app.common.ts#L49](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/app.common.ts#L49) [server/src/middleware/auth.guard.ts#L68-79](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/middleware/auth.guard.ts#L68-L79) [server/src/dtos/env.dto.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/dtos/env.dto.ts)
- server.requestTimeout is set to 24 hours (24*60*60*1000) to support huge uploads/downloads; combined with 10mb JSON body limits this leaves long-lived socket exposure on the API port. — [server/src/app.common.ts#L58-92](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/app.common.ts#L58-L92)
- Password auth uses bcrypt compare against LOGIN_DUMMY_HASH when the email is unknown (constant-time anti-enumeration) — the dummy hash constant lives in constants.ts and weakening/removing it would reopen user enumeration. — [server/src/services/auth.service.ts#L66-74](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/services/auth.service.ts#L66-L74) [server/src/constants.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/constants.ts)
- Shared-link access is authorized via query parameters (key/slug) declared with ApiQuery in the @Authenticated decorator, meaning bearer-equivalent secrets can appear in URLs, access logs, and referrers. (inferred) — [server/src/middleware/auth.guard.ts#L49-54](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/middleware/auth.guard.ts#L49-L54) [server/src/services/shared-link.service.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/services/shared-link.service.ts)
- Media processing shells out to ffmpeg/ffprobe (fluent-ffmpeg) and exiftool-vendored on user-uploaded files; media.repository.ts builds ffmpeg calls with niceness 10 — a large blast radius if argument handling or the bundled jellyfin-ffmpeg is compromised. — [server/src/repositories/media.repository.ts#L2-44](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/repositories/media.repository.ts#L2-L44) [server/src/repositories/media.repository.ts#L423-450](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/repositories/media.repository.ts#L423-L450) [mise.toml](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/mise.toml)
- The plugin host instantiates extism WASM plugins pooled via generic-pool and calls plugin methods by name; external plugin installation is env-gated (IMMICH_ALLOW_EXTERNAL_PLUGINS) and mis-set values expand supply-chain surface. — [server/src/repositories/plugin.repository.ts#L1-45](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/repositories/plugin.repository.ts#L1-L45) [server/src/repositories/plugin.repository.ts#L208-261](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/repositories/plugin.repository.ts#L208-L261) [server/src/dtos/env.dto.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/dtos/env.dto.ts)
- Startup is stateful and lock-coupled: the supervisor busy-polls pg_try_advisory_lock(MaintenanceOperation), DatabaseService runs migrations under DatabaseLock.Migrations, and nightly jobs require DatabaseLock.NightlyJobs — stuck locks or split-brain DBs can wedge boot or scheduling. — [server/src/main.ts#L66-92](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/main.ts#L66-L92) [server/src/services/database.service.ts#L67](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/services/database.service.ts#L67) [server/src/services/queue.service.ts#L51](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/services/queue.service.ts#L51)
- Any non-zero worker exit kills the API child (SIGTERM) and exits the supervisor with that code — a crash in a background job can take down the whole API unless orchestrated restart policies compensate. — [server/src/main.ts#L149-162](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/main.ts#L149-L162)
- Every route must declare @Authenticated() or AuthGuard throws at request time ('Route X does not declare @Authenticated()'); this is a strong default-deny but means a missing decorator yields runtime 500s rather than compile errors. — [server/src/middleware/auth.guard.ts#L99-103](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/middleware/auth.guard.ts#L99-L103) [server/src/app.module.ts#L53](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/app.module.ts#L53)
- Library watching uses chokidar over admin-configured filesystem paths and writes discovered files into the managed tree; a hostile or malformed library path expands file-IO reach beyond the media root. — [server/src/services/library.service.ts#L41-85](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/services/library.service.ts#L41-L85) [server/package.json#L72](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/package.json#L72)
- Dev/test convenience defaults embed credentials: DB_URL defaults to postgres://postgres:postgres@localhost:5432/immich in migration scripts and sync-open-api.ts hardcodes the same DSN — fine for local dev but a footgun if reused. — [server/package.json#L30-38](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/package.json#L30-L38) [server/src/bin/sync-open-api.ts](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/bin/sync-open-api.ts)
- Postgres's 65535 bind-parameter limit is worked around via the @Chunked decorator (DATABASE_PARAMETER_CHUNK_SIZE 65_500); repository methods handling ID sets rely on authors remembering to decorate — a missed site yields runtime driver errors on large batches. — [server/src/decorators.ts#L28-108](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/server/src/decorators.ts#L28-L108)
- Mobile pins a git dependency (native_video_player) to a commit ref and consumes a generated openapi package — both are supply-chain and drift-sensitive integration points. (inferred) — [mobile/pubspec.yaml](https://github.com/immich-app/immich/blob/d6d094738366aaf4d2fcefdfe2702f50c72ba5b0/mobile/pubspec.yaml)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
