Files
SnapOtter/.env.example
T
SnapOtterandGitHub 079fcd2631 fix(security): close the gaps a full 2.0 re-audit left open (#620)
Follow-up to a full re-audit of the 2.0 tree. Most prior findings were already
fixed; this closes the ones that were not:

- SAML assertion replay: validateInResponseTo ifPresent plus a Redis-backed
  CacheProvider, so a captured signed assertion cannot be replayed. ifPresent
  keeps IdP-initiated SSO working.
- MFA login challenge burned after 5 wrong TOTP codes.
- api_keys.key_prefix indexed; the per-request lookup was a full table scan.
- MAX_AI_JOBS_PER_USER caps a user's in-flight single-file AI jobs (the AI pool
  runs at concurrency 1). Batch and pipeline AI stay uncapped.
- MAX_WORKSPACE_SIZE_GB enforced instead of being dead config.
- SUBPROCESS_MEMORY_LIMIT_MB (default off) for the native media and doc engines;
  not applied to the AI sidecar.
- SVG sanitizer closes unquoted and whitespace-prefixed javascript: hrefs and
  the animateTransform/animateMotion/handler/mpath elements.
- Windows-style paths stripped from error output to match the Sentry scrubber.
- Postgres and Redis compose services get cap_drop plus pids_limit and cpus.
- .env.example ships MAX_SVG_SIZE_MB=50 (0 disabled the cap).

Adds security-focused unit and integration tests. typecheck, biome, and the
full unit and integration suites pass.
2026-07-23 00:18:16 +08:00

117 lines
4.4 KiB
Bash

# Server
PORT=1349
AUTH_ENABLED=true
DEFAULT_USERNAME=admin
DEFAULT_PASSWORD=admin
STORAGE_MODE=local
# Cleanup
FILE_MAX_AGE_HOURS=72
CLEANUP_INTERVAL_MINUTES=60
# Upload & Batch (0 = unlimited)
MAX_UPLOAD_SIZE_MB=0
MAX_BATCH_SIZE=0
CONCURRENT_JOBS=0
MAX_MEGAPIXELS=0
# Max single-file AI jobs one user may have in flight (AI pool is concurrency 1; 0 = unlimited)
MAX_AI_JOBS_PER_USER=5
# Optional per-process memory cap (MB) for the native media/doc engines (0 = disabled; container limit is the primary backstop)
SUBPROCESS_MEMORY_LIMIT_MB=0
# Max frames processed by animated background removal (0 = unlimited)
GIF_BG_MAX_FRAMES=150
# Rate limiting (0 = disabled)
RATE_LIMIT_PER_MIN=0
# Users (0 = unlimited)
MAX_USERS=0
# Processing (0 = auto/unlimited)
MAX_WORKER_THREADS=0
PROCESSING_TIMEOUT_S=0
MAX_PIPELINE_STEPS=0
MAX_CANVAS_PIXELS=0
# SVG has no "auto" sizing: 0 here disables the pre-parse size cap entirely.
# Ship the code default (50 MB) so copying this file does not remove the guard.
MAX_SVG_SIZE_MB=50
MAX_LOGO_SIZE_KB=2048
MAX_SPLIT_GRID=100
MAX_PDF_PAGES=0
MAX_VIDEO_DURATION_S=0
MAX_AUDIO_DURATION_S=0
MAX_VIDEO_BITRATE_KBPS=0
LIBREOFFICE_TIMEOUT_S=120
# Engine binary overrides (default: $PATH lookup)
# FFMPEG_PATH=
# FFPROBE_PATH=
# QPDF_PATH=
# SOFFICE_PATH=
# PDFCPU_PATH=
# SNAPOTTER_HW_ACCEL= # nvenc|vaapi: hardware encoder family (default software)
# AI tools fetch missing model files automatically (public model weights only,
# never user data). Set 0 for airgapped deployments to guarantee zero outbound
# fetches; missing models then produce actionable errors instead of downloads.
SNAPOTTER_ALLOW_MODEL_DOWNLOAD=1
SESSION_DURATION_HOURS=168
LOGIN_ATTEMPT_LIMIT=10
# Set to true in CI/dev to skip the forced password-change on the default admin
# SKIP_MUST_CHANGE_PASSWORD=false
# DB_PATH removed in 2.0 -- see DATABASE_URL below
WORKSPACE_PATH=./tmp/workspace
FILES_STORAGE_PATH=./data/files
DEFAULT_THEME=light
DEFAULT_LOCALE=en
APP_NAME=snapotter
# --- 2.0 foundation ---
# Postgres connection (required; this default matches docker-compose.dev.yml)
# Start the dev stack first: docker compose -f docker-compose.dev.yml up -d
DATABASE_URL=postgres://snapotter:snapotter@localhost:5432/snapotter
# Redis connection (required; this default matches docker-compose.dev.yml)
REDIS_URL=redis://localhost:6379
# Startup grace: wait this long (ms) for Postgres/Redis to accept connections
# before failing. Lets a dependency that is still booting recover instead of
# crash-looping. 0 = try once, fail fast.
DB_STARTUP_TIMEOUT_MS=30000
# Job spine tuning
SYNC_WAIT_MS=8000 # sync-response window (ms) before returning 202
JOBS_RETENTION_DAYS=30 # completed-job metadata TTL (days)
AUDIT_RETENTION_DAYS=0 # audit-log TTL (0 = keep forever)
LOG_DIR=./data/logs # rotating log ring for support bundles
# JOB_TIMEOUT_FAST_S=120 # timeout for fast pools (image, docs), seconds
# JOB_TIMEOUT_LONG_S=7200 # timeout for long pools (ai, media), seconds
# SCRATCH_PATH= # worker scratch dir (default: OS tmpdir/snapotter-scratch)
# Prometheus metrics: GET /api/v1/metrics requires an authenticated admin
# (scrapers need a session cookie or API key with system:health permission).
# --- Analytics ---
# Basic analytics are included by default. SnapOtter works normally without them.
# To disable: docker compose build --build-arg SNAPOTTER_ANALYTICS=off
# Runtime kill switch: set to 0 to disable ALL telemetry (Sentry + PostHog)
# for this instance without rebuilding. The in-app admin toggle does the same
# from Settings; this env var also covers boot-time crashes and CI fleets.
# ANALYTICS_ENABLED=false is honored as an alias (0/off also work).
# SNAPOTTER_TELEMETRY=1
# Label this instance's error reports (shows as the Sentry environment).
# SNAPOTTER_ENV=production
# One-time SQLite import on first boot (1.x upgrade path). Leave unset normally.
# SQLITE_MIGRATE_PATH=/data/snapotter.db
# --- OpenTelemetry Distributed Tracing (enterprise only) ---
# Requires a valid enterprise license with distributed_tracing feature.
# Set OTEL_EXPORTER_OTLP_ENDPOINT to enable. All other vars are optional.
# Docs: https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/
# OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
# OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
# OTEL_EXPORTER_OTLP_HEADERS=
# OTEL_SERVICE_NAME=snapotter-api
# OTEL_TRACES_SAMPLER=parentbased_always_on
# OTEL_TRACES_EXPORTER=otlp