Commit Graph
139 Commits
Author SHA1 Message Date
SnapOtterandGitHub 19d9ed181a fix(audio): low-samplerate ogg encode + post-2.0 QA hygiene
Quality-VBR ogg (libvorbis -q:a) fixes 8 kHz 'encoder setup failed' in both ogg paths; drop empty COOKIE_SECRET ENV (app auto-generates); emit real bundle extractedSize; fix stale image-pad/compress-pdf QA specs.
2026-06-20 10:41:50 +08:00
SnapOtterandGitHub 9b64a96bcd fix(docker): restore Postgres/Redis in GPU compose stack + pause image publishing
Restore Postgres 17 + Redis 8 services in docker-compose-gpu.yml; re-add if:false publish pause on the release.yml docker job (main had lost it).
2026-06-19 18:39:35 +08:00
SnapOtterandGitHub 3b50bcdc5c fix(ai-bundles): repair bundle build + publish pipeline (deepsafe repo, CPU provider, manifest)
Bundle build/publish fixes: CPUExecutionProvider in rembg build, pip/import/arm64 deps, hf-CLI publish to deepsafe/feature-bundles, real manifest sha256+sizes, installer fallback repo.
2026-06-19 18:34:33 +08:00
SnapOtter d46b82e11e test: run the docker suite without the coverage gate
pnpm test:docker ran pnpm test:ci (vitest --coverage), but the lean test image deliberately skips binary-gated tools (AI model bundles, LibreOffice, etc.), so it can never meet the host-calibrated coverage thresholds -- the container exited non-zero on coverage even with zero test failures. Point the compose command at vitest run so test:docker is a clean functional pass/fail gate; coverage stays enforced on host CI where every tool is present.
2026-06-17 14:28:41 +08:00
SnapOtter 2d2f23cff0 test: install enterprise S3 dep and open PS coders in docker test image
Two test-image gaps surfaced by a full pnpm test:ci run:

- s3-storage.test.ts imports @aws-sdk/client-s3 (an enterprise dependency) at module load, but Dockerfile.test never copied packages/enterprise/package.json before pnpm install, so the suite failed to collect. Copy it so the dep installs; the suite then skips cleanly when MinIO is absent.

- EPS batch decode returned 422: ImageMagick reads EPS through the Ghostscript PS coder, but policy.xml left PS/PS2/PS3 at rights=none (only EPS was opened), so convert refused with a policy error before Ghostscript ran. Open the PostScript coders too.
2026-06-17 14:28:41 +08:00
SnapOtter 63a2d309ce test: honor SYNC_WAIT_MS in docker tests instead of clobbering it
tests/setup/per-fork-env.ts hardcoded SYNC_WAIT_MS=30000 on every fork, overriding whatever the container set, so the docker test image could never grant heavy ops a wider sync window. A 12MP stress-image enhance takes ~34s on the macOS Docker VM (Sharp runs 2-3x slower there), just past the 30s window, so the factory returned 202 and three sync-asserting image-enhancement tests failed.

Honor a higher SYNC_WAIT_MS when provided (30s floor preserved for host/CI), raise it to 120s in docker-compose.test.yml, and make the vitest test/hook timeouts env-overridable so a slow-but-correct job returns 200 rather than tripping a framework timeout. Host and CI behavior is unchanged.
2026-06-17 14:28:41 +08:00
SnapOtter 1f5b222267 test: fix docker test-image env and container-specific test guards
Make the full pnpm test:docker suite pass the env-dependent tests (~85 failures):
- Dockerfile.test: ENV LD_LIBRARY_PATH=/usr/local/lib so the built libheif 1.21 is not shadowed by the base image's older system libheif (heif-dec failed with an undefined-symbol error -> 'No HEIF decoder found' on 72 HEIF tests); add libjxl-tools (JXL) and ghostscript + the ImageMagick policy.xml EPS allow-edit.
- docker-compose.test.yml: SYNC_WAIT_MS=30000 so sync-wait image tools do not fall back to 202 under single-container contention (10 tests).
- install_feature.py: guard tarfile.extractall(filter='data') behind Python>=3.12 (bookworm ships 3.11); the manual entry guards already protect.
- feature-status.test.ts / docker-file-secrets.test.ts: skip the two cases that cannot hold inside the container (/.dockerenv always present; root bypasses chmod). Verified on host: all still pass.
2026-06-17 14:28:41 +08:00
SnapOtterandGitHub d8cf979d4b fix: resolve 18 QA-discovered bugs across tools, previews, and the AI pipeline (#242)
Exhaustive QA sweep of all 157 tools. Fixes: CSP blob media, csv-excel ExcelJS interop, ocr-pdf segfault, chart-maker upload, non-PDF doc preview, RAW decode, merge-tool multi-file path, html-to-image chromium, ogv/wma/amr/ac3 preview fallbacks, meme/gif/stabilize codecs, nav+home a11y. Plus orphan-format and test-debt cleanup, the AI bundle build script, and a reusable Playwright QA harness under tests/qa/.
2026-06-15 22:26:24 +08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
771a884ac5 chore(deps): bump debian from ed4fcc4 to 49ba348 in /docker (#235)
Bumps debian from `ed4fcc4` to `49ba348`.

---
updated-dependencies:
- dependency-name: debian
  dependency-version: bookworm
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 15:02:14 +08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
eedecbb67e chore(deps): bump mwader/static-ffmpeg from 8.0 to 8.1.1 in /docker (#234)
Bumps mwader/static-ffmpeg from 8.0 to 8.1.1.

---
updated-dependencies:
- dependency-name: mwader/static-ffmpeg
  dependency-version: 8.1.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 15:02:11 +08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
21ed984c92 chore(deps): bump node from 1031993 to 2d178f2 in /docker (#233)
Bumps node from `1031993` to `2d178f2`.

---
updated-dependencies:
- dependency-name: node
  dependency-version: 22-bookworm
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 15:02:03 +08:00
SnapOtter b1e94bd988 fix: build script venv handling and lint fixes
- Use /opt/venv directly when --entrypoint bash bypasses entrypoint.sh
- Use sys.executable for all pip calls (not bare pip)
- Override entrypoint in CI workflow to avoid startup banner
- Fix Biome formatting (template literals, try/catch blocks)
2026-06-13 20:48:27 +08:00
SnapOtter 52b940585a feat: add bundle build script for CI 2026-06-13 16:41:31 +08:00
SnapOtter a4ac7cf7d6 feat: bump feature manifest to v2 with archive metadata 2026-06-13 16:27:24 +08:00
SnapOtter 39543d6512 fix(docker): make venv bootstrap upgrade-aware with pip-freeze stamp
On 1.x-to-2.0 upgrade the /data/ai/venv already existed with stale
packages, so the entrypoint skipped the cp from /opt/venv and 2.0
Python tools failed with ImportError (#85).

Dockerfile now writes a SHA-256 of `pip freeze` to /opt/venv/.venv-version
at build time. The entrypoint compares that stamp against the copy on the
volume; on mismatch it does a clean nuke + re-copy and resets
installed.json so the UI correctly shows bundles as needing reinstall.
Models in /data/ai/models survive the refresh, so bundle reinstall only
reruns pip (model downloads are idempotent).

Backward compatible: if the image has no stamp (old build), the
entrypoint falls through to the existing skip-if-exists behavior.
2026-06-13 10:31:22 +08:00
SnapOtter 51666cdd5f feat(tools): 2.0 phase 5 wave 5b - ai pool: ocr-pdf, transcription, background composites (5 tools) (#226) 2026-06-13 10:19:47 +08:00
SnapOtter 6e1b9865f1 feat(tools): 2.0 phase 5 wave 5a - image gap-fill (11 tools) (#225) 2026-06-13 10:19:16 +08:00
SnapOtter fc7c1f850e feat(tools): 2.0 phase 5 wave 4 - office, ebooks, data, archives (14 tools) (#224) 2026-06-13 10:19:11 +08:00
SnapOtter 2f39e38162 feat(tools): 2.0 phase 5 wave 2 - pdf depth (21 tools) (#220) 2026-06-13 10:18:55 +08:00
SnapOtter d647d8ed19 feat(modality)!: SnapOtter 2.0 phase 3 modality framework: media/doc engines, pool routing, display modes (#218) 2026-06-13 10:18:39 +08:00
SnapOtter c451b939c7 feat(jobs)!: SnapOtter 2.0 phase 2 job spine: async queues, worker pools, object storage, admin dashboard (#217) 2026-06-13 10:17:13 +08:00
SnapOtterandGitHub 1c724d5d21 feat(db)!: SnapOtter 2.0 phase 1 foundation: postgres, migrator, compose stack (#216)
* feat(infra): add dev compose stack with postgres and redis

* fix(infra): comment dev env defaults until wired; harden dev compose restart and start_period

* chore(deps): add pg driver and testcontainers for postgres migration

* feat(db): translate schema to drizzle pg-core (timestamptz, boolean, pgEnum, jsonb)

Schema translation (apps/api/src/db/schema.ts):
- sqlite-core -> pg-core, all 10 tables preserved 1:1
- integer(mode:'timestamp') -> timestamp({ withTimezone: true })
- integer(mode:'boolean') -> boolean
- jobs.status text enum -> pgEnum('job_status') with same 4 values
- 7 columns changed from text to jsonb: jobs.inputFiles, jobs.settings,
  pipelines.steps, apiKeys.permissions, roles.permissions,
  auditLog.details, userFiles.toolChain
- settings.value stays text, jobs.error stays text, jobs.progress stays real

jsonb call-site sweep (removed JSON.stringify on writes, JSON.parse on reads):
- apps/api/src/routes/roles.ts: permissions read/write (3 sites)
- apps/api/src/routes/api-keys.ts: permissions write + read (2 sites)
- apps/api/src/routes/audit-log.ts: details read (1 site)
- apps/api/src/routes/pipeline.ts: steps write + read (2 sites)
- apps/api/src/routes/progress.ts: inputFiles write (2 sites)
- apps/api/src/routes/tool-factory.ts: toolChain read + write (2 sites)
- apps/api/src/routes/user-files.ts: toolChain read + write (4 sites)
- apps/api/src/permissions.ts: roles.permissions read (1 site)
- apps/api/src/lib/audit.ts: details write (1 site)
- apps/api/src/plugins/auth.ts: apiKeys.permissions read (1 site)

* refactor(db): type jsonb columns via $type and note raw CTE conversion requirements

* feat(db): archive sqlite migrations and generate postgres baseline

* chore(db): dockerignore legacy migrations, add archive breadcrumb, fix trailing newline

* feat(db): pg pool connection, advisory-locked boot migrations, DATABASE_URL config

* fix(db): friendly fatal on unreachable postgres, idempotent closeDb, lock-key convention note

* refactor(db): async drizzle calls in plugins, lib, permissions

* fix(api): analytics never throws, typed permission guard, single-query session invalidation

* refactor(db): async drizzle calls across all routes and bootstrap

Convert every route file and index.ts from sync SQLite drizzle
patterns to async node-postgres drizzle:

- .all() removed (bare await on select)
- .get() converted to destructured [row] = await ...
- .run() removed (bare await on insert/update/delete)
- .changes replaced with .rowCount (null-guarded) in progress.ts
- sqlite import removed from user-files.ts; raw CTEs converted to
  await db.execute(sql`...`) with postgres-dialect recursive CTEs
- ChainRow types updated: tool_chain is parsed jsonb (string[] | null),
  created_at is Date (timestamptz) with no * 1000 conversion
- All requirePermission() guard calls awaited (security: unawaited
  async guard returns truthy Promise, bypassing permission check)
- All hasEffectivePermission() and getPermissions() calls awaited
- All auditLog() calls awaited (preserves write-before-response order)
- trackEvent() and captureException() left un-awaited (fire-and-forget
  by design, guaranteed never-throw)
- ensureAnonymousUser(), startCleanupCron(), recoverStaleJobs() awaited
  in bootstrap sequence
- ensureInstanceId() and ensureDefaultSettings() made async

Files converted: 14 (index.ts + 12 route files + tools/index.ts)

* fix(db): await async checkStorageQuota in user-files upload/save routes

* fix(db): await checkStorageQuota in save-result route (missed second call site)

* feat(db): sqlite-to-postgres migrator with CLI and first-boot import

* fix(db): migrator error context, honest force semantics, boot-hook fatal, null-variance tests

* test: run suite against per-file postgres databases via testcontainers

- Add tests/global-setup.ts: spins up a Postgres testcontainer,
  creates a migrated template database once per vitest run.
- Rewrite tests/setup/per-fork-env.ts: each test file (forks pool)
  clones the template into its own database via CREATE DATABASE ...
  TEMPLATE, preserving the same per-file isolation granularity.
- Update vitest.config.ts: add globalSetup, pg alias, update comment.
- Fix tests/integration/test-server.ts: remove DB_PATH mkdir, async
  runMigrations, async db operations, remove SQLite WAL checkpoint.
- Fix 21 unit test db/index mocks: add pool and closeDb exports.
- Fix 8 unit test files: add async/await for now-async permission,
  audit, and analytics functions.
- Fix 18 integration test files: convert sync .run()/.all()/.get()
  to async drizzle patterns, add async to callbacks.
- Production change: apps/api/src/routes/teams.ts: cast COUNT(*)
  to ::int so Postgres returns a number instead of bigint string.

* fix(db): seed built-in roles, reject NUL bytes, cast COUNT, serialize job persists

- Seed built-in roles (admin, editor, user) at boot via ensureBuiltinRoles()
  with onConflictDoNothing, restoring data that legacy SQLite migration 0007
  provided via INSERT statements (the pg baseline is DDL-only).
- Reject NUL bytes in login credentials with 401 (postgres rejects \x00 in
  text columns; valid usernames never contain NUL, matching 1.x behavior).
- Cast COUNT(*)::int in user-files, audit-log, and roles listing queries so
  postgres returns a JS number instead of bigint-as-string.
- Serialize fire-and-forget job progress DB writes per jobId so the final
  "completed" status is never overwritten by a late-arriving "processing"
  write (race condition exposed by async postgres round-trips).

* test: fix teams race, seed roles in test server, poll for job status

- Add missing await to resetTeams() in teams PUT beforeEach (the async
  delete raced with the subsequent insert under postgres).
- Call ensureBuiltinRoles() in test server bootstrap so integration tests
  have the same built-in roles as production.
- Replace fixed 100ms flushPersist delay with a polling helper that waits
  for terminal job status, eliminating timing-dependent failures caused by
  postgres network round-trip latency.

* test: make heic temp-file cleanup assertion resilient to concurrent workers

Use a set-based diff instead of raw file count when checking that
decodeHeic cleans up temp files. Other concurrent test workers can
create heic-in-*/heic-out-* files in the shared tmpdir, inflating the
"after" count and causing spurious failures under full-suite load.

* fix(db): align builtin-role seed to post-0010 legacy state; test polish

* feat(docker): three-container compose (app, postgres, redis) with boot wait and migrations

* fix(docker): set TEST_DATABASE_URL so containerized tests skip testcontainers

* chore(docker): test compose project name, clearer 1.x upgrade comment, unref probe timer

* feat(enterprise): enforce D15 license boundary; move s3 storage into packages/enterprise

* fix(enterprise): restore lazy aws-sdk loading; community installs load no s3 code at boot

* fix(enterprise): boundary check catches dynamic imports; document getS3 concurrency

* feat(db)!: SnapOtter 2.0 phase 1 foundation: postgres, migrator, compose stack

BREAKING CHANGE: SQLite is no longer the runtime database. Deployments now
require Postgres (and Redis, used from phase 2). Existing installs migrate
with SQLITE_MIGRATE_PATH or 'pnpm --filter @snapotter/api migrate:sqlite'.

* fix(ci): postgres service + fresh e2e database per run; ignore unfixable torch CVE-2025-3000
2026-06-13 10:15:23 +08:00
SnapOtterandGitHub 4ec39c556f test: testing overhaul -- CI e2e gates, parallel suites, generated matrices, mutation testing (#215)
Closes the "e2e never runs in CI" hole. Adds per-PR e2e smoke gate,
nightly full-suite workflows, parallel vitest forks (per-fork DBs),
Playwright parallel/serial/visual projects against production builds,
metadata-generated test suites (drift guards, hostile inputs, format
matrix, pairwise settings, property-based fuzz), Stryker mutation
testing, Schemathesis API fuzz, coverage ratchet, and fixes for three
session-poisoning bugs that caused 200+ serial-bucket failures.

Bug fix included: favicon/split/bulk-rename could hang clients forever
when ZIP streaming failed after reply.hijack().
2026-06-10 22:01:13 +08:00
SnapOtter 3b8d529b44 fix(ci): revert rembg to 2.0.62 (2.0.75 requires numpy>=2.3)
rembg 2.0.75 pulls in numpy>=2.3.0 which conflicts with our pinned
numpy==1.26.4 and would break the entire AI dependency chain. The two
rembg CVEs (SSRF + path traversal) are in its server/CLI components
which we don't use; they're already in the pip-audit ignore list.
2026-06-10 21:21:39 +08:00
SnapOtter 8792080982 fix(deps): patch Dependabot security alerts
- Pillow 11.1.0 -> 12.2.0 (6 CVEs: OOB writes, decompression bomb, DoS)
- rembg 2.0.62 -> 2.0.75 (SSRF + path traversal in server component)
- @fastify/static ^8.1.0 -> ^9.1.3 (path traversal + route guard bypass)
- Remove redundant @fastify/static pnpm override
- Dismiss stale esbuild alert (already at 0.28.0)
- Dismiss file-type alert (16.5.4 is dev-only via @types/potrace)
2026-06-10 19:08:08 +08:00
SnapOtter d2fff1f87d fix(security): bump Go 1.25 and Pillow 12.2.0 for new Trivy CVEs
- Go 1.24 -> 1.25: fixes CVE-2026-25679 (HIGH: net/url IPv6 parsing)
- Pillow 12.1.1 -> 12.2.0: fixes CVE-2026-40192 (HIGH: decompression bomb DoS)
2026-06-08 17:32:34 +08:00
SnapOtter 0c58db2417 fix(security): update Go 1.24 and Pillow 12.1.1 for Trivy CVEs
- Bump Go from 1.23 to 1.24 to fix CVE-2025-68121 (CRITICAL:
  crypto/tls certificate validation) and CVE-2025-61726 (HIGH:
  net/url memory exhaustion) in the caire binary.
- Bump Pillow from 11.1.0 to 12.1.1 to fix CVE-2026-25990 (HIGH:
  out-of-bounds write via crafted image).
2026-06-08 17:06:07 +08:00
SnapOtter e0eb2e2075 fix(ci): add curl retry for libheif download and update release notes
- Add --retry 3 --retry-delay 5 to libheif curl in Dockerfile to
  handle transient GitHub CDN 504 errors on arm64 CI runners.
- Update release notes: use @ mentions, remove double-dash separators.
2026-06-08 16:17:28 +08:00
SnapOtter 1d7bc00d2d fix(docker): use CUDA 12.6 index for PaddlePaddle GPU and revert version
- fix(ocr): change paddlepaddle-gpu from --extra-index-url to --index-url
  for the CUDA 12.6 package index. With --extra-index-url, pip could
  resolve from PyPI (CUDA 11 build) instead of the cu126 index, causing
  "libcusolver.so.11: undefined symbol" errors on CUDA 12 containers.

- revert version to 1.17.1 (v1.17.2 release was deleted)
2026-06-08 14:36:59 +08:00
SnapOtter 9a1d3d25f4 fix(docker): resolve 4 release-blocking issues from validation
- fix(rate-limit): treat RATE_LIMIT_PER_MIN=0 as unlimited (50k/min)
  instead of blocking all requests. @fastify/rate-limit interprets
  max:0 as "allow zero requests," breaking fresh container startups.

- fix(docker): add libgles2 for MediaPipe face detection tools.
  blur-faces, red-eye-removal, enhance-faces, and passport-photo
  failed with "libGLESv2.so.2 not found" on all headless containers.

- fix(docker/arm64): remove conflicting system libheif1 to avoid
  ABI symbol mismatch with our custom libheif 1.21.2 build.
  heif-convert failed with "undefined symbol: heif_get_plugin_directories."

- fix(docker/arm64): pre-install wheel+setuptools in base Python venv
  so basicsr can build from source on arm64 (no pre-built wheel).
  This unblocks upscale-enhance and photo-restoration bundles.
2026-06-08 14:07:55 +08:00
SnapOtter ace41168bc fix(security): harden API against pentest findings
- Default TRUST_PROXY=false to prevent XFF rate limit bypass (PT-01)
- Return 400 instead of 500 on malformed JSON input (PT-03)
- Default MAX_PIPELINE_STEPS=20 to prevent DoS (PT-04)
- Validate clientJobId length (max 128) across all routes (PT-06)
- Add security headers to all reply.hijack() streaming responses (PT-07)
- Sanitize usernames in audit log to prevent stored XSS (PT-08)
- Block TRACE method with 405 response (PT-10)
- Add 429 RateLimited response to OpenAPI spec (PT-12)
- Default MAX_SVG_SIZE_MB=50 to limit SVGZ decompression (PT-13)
- Pin Dockerfile base images by digest
- Sanitize OIDC IdP error and sub claim in audit log
- Sync Docker compose/Dockerfile defaults with env.ts
2026-06-07 21:54:27 +08:00
SnapOtterandGitHub 5a32e29b8c fix(security): security audit and hardening (#207)
* fix(security): harden SVG sanitizer, rate limiting, and analytics defaults

- SVG: add control-char stripping in href values to block whitespace/null-byte
  obfuscated javascript: URIs; block <feImage> with external href (SSRF via
  SVG filter primitives); expand test suite to 32 inline bypass payloads
- Rate limiting: add per-route limits on tool endpoints (60/min) and batch
  (20/min); fix compose files defaulting RATE_LIMIT_PER_MIN to 0 which mapped
  to 50,000 in code; simplify rate limit registration to use env.ts default
- Analytics: default ANALYTICS_ENABLED to false so self-hosters do not
  unknowingly send telemetry
- Docker: add --max-time 5 and -s flags to compose healthcheck curl commands

* fix: remove stale login limit bypass, reduce error log noise, clean up fixtures

- Fix getLoginAttemptLimit() ignoring LOGIN_ATTEMPT_LIMIT when global rate
  limit exceeded 1000/min, which let the global limit override the stricter
  per-route login brute-force protection
- Downgrade rate limit 429 responses from error to warn level in the global
  error handler to avoid log noise and unnecessary Sentry reports
- Log 4xx client errors at warn level instead of error level
- Remove 11 orphaned SVG attack fixture files replaced by inline test payloads
2026-06-07 10:43:52 +08:00
SnapOtter 4d7f67d2af feat: add playwright chromium to docker image for html-to-image 2026-06-06 21:45:39 +08:00
SnapOtterandGitHub 10a85cf37e feat: add Docker _FILE secret convention for sensitive env vars (#205)
Support reading secrets from mounted files instead of plain-text
environment variables, following the standard Docker/Kubernetes
convention used by MariaDB, Postgres, and Stirling-PDF.

Supported vars: DEFAULT_PASSWORD, S3_ACCESS_KEY_ID,
S3_SECRET_ACCESS_KEY, OIDC_CLIENT_SECRET, COOKIE_SECRET,
SNAPOTTER_LICENSE_KEY.
2026-06-06 21:18:14 +08:00
SnapOtter 66e503730d fix: resolve 6 production Sentry errors
- Prevent @fastify/static double-registration crash via decorateReply guard
- Fix non-ASCII filename header encoding (X-Output-Filename + RFC 5987 Content-Disposition)
- Add EACCES error handling to all startup mkdir calls with actionable messages
- Add WAL autocheckpoint and journal size limit to prevent unbounded SQLite growth
- Fix Python sidecar EPIPE handling to reject pending requests and trigger restart
- Ensure Docker entrypoint creates all subdirectories before chown
2026-06-06 16:05:59 +08:00
SnapOtterandGitHub f1aae73397 feat: pre-built release archives + AI install fixes (#202)
Add pre-built release archives (Linux amd64/arm64) to the release
workflow, published as GitHub Release assets. Each archive is a
self-contained tar.gz (~240MB) with built frontend, API source,
and production node_modules. Users extract and run without needing
pnpm build.

Also includes AI install manifest fixes for Proxmox/bare-metal users:
- Pin setuptools<75 for Python 3.13 basicsr compatibility
- Pre-install basicsr with --no-build-isolation before realesrgan
- Loosen mediapipe pins from == to >= for Python 3.13 wheels
- Add retry logic to HuggingFace model downloads
2026-06-05 18:42:30 +08:00
SnapOtterandGitHub c365fde599 fix: build libheif 1.21.2 from source for iPhone HEIC support (#183) (#199)
iPhone 15 Pro (iOS 18+) HEIC files include HDR gain maps as auxiliary
image references. Distro-packaged libheif (1.15-1.17) rejects these
with "Too many auxiliary image references". Build libheif v1.21.2 from
source in a new Dockerfile stage to fix decoding.

- Add libheif-builder stage with platform-matched bases (debian:bookworm
  for arm64, ubuntu:24.04 for amd64) to avoid shared-library ABI
  mismatches
- Replace libheif-examples distro package with source-built binaries
- Update Dockerfile.test with same libheif source build
- No application code changes needed (heic-converter.ts CLI interface
  is stable across versions)

Closes #183
2026-06-05 17:05:01 +08:00
SnapOtterandGitHub 32055e0b32 fix: correct Docker image name in compose files (#198)
Closes #182
2026-06-05 17:04:52 +08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
e4608991ef chore(deps): bump nvidia/cuda in /docker (#185)
Bumps nvidia/cuda from 12.6.3-cudnn-runtime-ubuntu24.04 to 12.9.2-cudnn-runtime-ubuntu24.04.

---
updated-dependencies:
- dependency-name: nvidia/cuda
  dependency-version: 12.9.2-cudnn-runtime-ubuntu24.04
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-04 22:16:35 +08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
7af141be02 chore(deps): bump node from e3ca095 to 1031993 in /docker (#184)
Bumps node from `e3ca095` to `1031993`.

---
updated-dependencies:
- dependency-name: node
  dependency-version: 22-bookworm
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-04 21:48:35 +08:00
SnapOtterandGitHub ec6ff3d8a8 chore: harden OpenSSF Scorecard from 4.3 to ~7.0 (#142)
Token-Permissions (0 -> 10): Set permissions: {} at workflow top level
across all 7 workflows, moved write scopes to per-job minimum.

SAST (0 -> 10): Added CodeQL workflow for JavaScript/TypeScript and
Python analysis on push, PR, and weekly schedule.

Vulnerabilities (0 -> ~8): Added 13 pnpm overrides to patch transitive
dependency vulnerabilities (38 -> 2 remaining, both in dev-only tools).

Pinned-Dependencies (5 -> 8-9): Pinned all Docker FROM images to SHA
digests, pinned pip-audit version in CI, pinned pip version in
Dockerfile.
2026-05-18 15:57:17 +08:00
SnapOtter 36431bce48 fix: improve AI feature install error handling and resource limits
Handle OOM kills (exit code 137) with actionable memory guidance,
filter ANSI/progress noise from error output, add --no-cache-dir to
pip installs, reduce download concurrency to 2, and bump default
container memory from 4g to 6g.
2026-05-18 10:19:43 +08:00
SnapOtter f86ef124c2 fix: grant admin role to anonymous user and add DEFAULT_TOOL_VIEW env var
When AUTH_ENABLED=false, the anonymous user was assigned the "user" role
which lacks settings:write permission, making all settings saves return
403. Since no admin exists when auth is disabled, settings were
permanently read-only. Promote the anonymous user to "admin" so the
single user has full control of the instance.

Also adds DEFAULT_TOOL_VIEW env var (sidebar|fullscreen) following the
existing DEFAULT_THEME pattern, seeded via ensureDefaultSettings() on
first boot.

Closes #135
2026-05-16 11:37:28 +08:00
SnapOtter b449bd1a56 fix(security): correct Docker rate limit defaults
- LOGIN_ATTEMPT_LIMIT: 500 -> 30 in Dockerfile (matching code default)
- RATE_LIMIT_PER_MIN=0 now means no global limit (50K ceiling) instead
  of 1 req/min, preserving backward compatibility with existing deploys
  while keeping per-route limits (login brute-force) active
2026-05-14 23:02:25 +08:00
SnapOtter ca2ef5b3f4 feat: add OIDC/SSO authentication (#3)
Add OpenID Connect (OIDC) authentication alongside existing
username/password login. Users can log in via any standards-compliant
OIDC provider (Keycloak, Authentik, Authelia, Google, Azure AD, Okta)
while preserving full backward compatibility.

- OIDC Fastify plugin with lazy discovery, PKCE, cookie-based sessions
- Login page OIDC button, auth hook updates, settings dialog badges
- 28 integration tests, OIDC setup guide with provider examples
- Fix pre-existing test failures (content-aware-crop, watermark, SVGZ)
- WAL checkpoint fix for SQLite test stability

Closes #3

# Conflicts:
#	apps/api/src/lib/env.ts
#	apps/api/src/routes/tools/watermark-image.ts
#	pnpm-lock.yaml
#	tests/integration/color-palette.test.ts
#	tests/integration/compare.test.ts
#	tests/integration/watermark-image.test.ts
2026-05-14 22:31:26 +08:00
SnapOtter 4e64ee2779 fix(security): comprehensive security audit and hardening
Auth: login rate limit 30/min (was 500), global rate limit 1000/min (was
unlimited), password/username max lengths on all Zod schemas, session
invalidation on role change, API key legacy scan bounded to 100 keys.

SVG: hardened regex sanitizer with CDATA stripping, XML entity decoding,
set/animate/iframe/embed blocking, comprehensive data: URI blocking,
use element external href blocking. 11 attack payload fixtures added.

SSRF: fixed DNS rebinding TOCTOU by pinning resolved IPs via custom
HTTP/HTTPS agents. Added 6to4 and NAT64 to blocked IPv6 ranges.

Docker: capability dropping (cap_drop ALL + minimal cap_add), resource
limits (4g/8g mem, 512/1024 pids), healthcheck timeout, password
removed from startup banner, default password warning comments.

Network: CSP and HSTS applied in all environments (not just production),
stack traces removed from all error responses, internal paths stripped
from error details, per-route rate limits on uploads (60/min) and URL
fetches (200/hour).

Files: exclusive temp file creation (O_EXCL), disk space circuit
breaker, per-user storage quotas, settings payload 64KB size guard.

Python sidecar: script name allowlist in dispatcher, minimal environment
for subprocess spawns.

Dependencies: fixed 6 production CVEs (drizzle-orm, fastify, fast-uri,
@fastify/static, next, archiver/lodash). Pinned all GitHub Actions to
SHA hashes.

114 security tests added. Full OWASP Top 10 penetration test matrix
verified against production Docker container (30/30 pass after
hardening).
2026-05-13 21:33:50 +08:00
SnapOtter 71496db6c5 feat(oidc): add OIDC environment variables to Docker configuration 2026-05-13 19:07:33 +08:00
SnapOtter c6a5d3f33e refactor: remove content-aware-crop tool
Remove the content-aware-crop tool entirely -- API route, frontend
settings component, e2e and integration tests, and all registry
entries.
2026-05-13 17:55:01 +08:00
SnapOtter 9763a10db4 fix: add tool registry entry and manifest for content-aware-crop 2026-05-11 21:23:47 +08:00
SnapOtter 7f131d99a6 fix: QA sweep -- SSE crash, memory leaks, HEIC Docker decode, TGA detection, lint cleanup
- Fix SSE write-after-end crash in progress.ts (remove callback before ending stream)
- Fix blob URL memory leaks: revoke processedPreviewUrl and old HEIC preview URLs
- Add AbortController to batch fetch in use-tool-processor and use-pipeline-processor
- Fix TGA format misidentified as CUR (extension overrides magic bytes)
- Add libheif-plugin-libde265 to Docker for HEIC/HEIF decode support
- Remove unused imports and state (AppLayout, setSampledColor, useEffect)
- Fix non-null assertions in meme-text-renderer and meme-generator
- Fix confusing void type in meme-templates
- Remove unnecessary useEffect deps in adjustments-panel
- Fix Playwright strict mode violations in 5 E2E tests
2026-05-09 13:55:00 +08:00