Commit Graph
945 Commits
Author SHA1 Message Date
SnapOtter d1f2f63453 fix: add 120s timeout to all HEIF/HEIC integration tests for CI
Comprehensively adds { timeout: 120_000 } to every HEIF/HEIC
integration test across 40 test files. HEIF decoding on CI runners
is significantly slower than local, causing sporadic timeouts at
the default 5s or previous 60s limits.
2026-05-01 19:51:47 +08:00
SnapOtter 6a44c455a3 fix: remove printf banner from docker commands in docs
The printf welcome banner was overflowing code blocks on
docs.snapotter.com, making docker commands hard to read and copy.
2026-05-01 19:42:11 +08:00
SnapOtter 332c79f4ce fix: increase all HEIF/HEIC integration test timeouts to 120s for CI
The sharpening HEIF test was timing out at 60s in GitHub Actions.
Bumps all remaining HEIF/HEIC test timeouts from 60s to 120s across
14 integration test files for consistency with vectorize.
2026-05-01 19:05:51 +08:00
SnapOtter 66211ed6a7 fix: resolve 20 Sentry issues and fix navbar test flakiness
Sentry fixes:
- Only send 5xx errors to Sentry (was sending 4xx rate-limit, media type errors)
- Encode non-ASCII chars in X-Output-Filename header (encodeURIComponent)
- Handle FK constraint failures gracefully in file upload, pipeline save, API keys
- Harden getDirSize against ENOENT race on readdirSync

Test fixes:
- Wrap navbar test renders in act() to flush async useEffect state updates
- Add useEffect cleanup to navbar to prevent state updates on unmounted component
- Fixes timeout when running in full test suite
2026-05-01 19:01:05 +08:00
SnapOtter ff8dcf63c7 fix: resolve 14 security, correctness, and robustness issues found during QA sweep
Security fixes:
- Add auth + ownership check to thumbnail endpoint (was unauthenticated)
- Validate ExifTool fieldsToRemove against safe tag name pattern
- Add SVG sanitization to pipeline execute and batch endpoints
- Replace basename() with sanitizeFilename() in 16 tool routes
- Escape SQL LIKE wildcards in file search to prevent pattern injection
- Improve settings HTML tag validation pattern

Bug fixes:
- Skip autoOrient for SVG inputs in pipeline (prevents misinterpretation)
- Remove double-encode in compress targetSize (was degrading quality)
- Fix bg-effects alpha value from 255 to 1.0 (Sharp expects float)
- Guard download stream error handler against headers-already-sent race
- Use O_EXCL atomic file creation for install lock (fixes TOCTOU race)
- Truncate collage file array to template image count

UX fixes:
- Accept empty JSON bodies on POST endpoints (install/uninstall)
- Custom JSON content type parser that treats empty body as {}
2026-05-01 18:11:49 +08:00
SnapOtter 1a2288f99b chore: standardize all contact emails to contact@snapotter.com
Replace security@snapotter.local and snapotter.hq@gmail.com with the
official contact@snapotter.com address across SECURITY.md,
CODE_OF_CONDUCT.md, and README.md.
2026-05-01 17:56:10 +08:00
SnapOtter 84f24b8736 chore: add Discord community links across README, docs, and landing page
- README: Discord badge in header + community section link
- Docs: GitHub and Discord social links in VitePress config
- Landing: Discord in navbar, footer, open-source section, pricing copy
2026-05-01 16:13:38 +08:00
SnapOtter b50e1e0fb3 fix: increase vectorize HEIF test timeout to 120s for CI
Vectorize + HEIF decoding exceeds 60s on CI runners because potrace
(CPU-intensive bitmap tracing) runs on top of slow HEIF decoding.
2026-05-01 15:24:19 +08:00
SnapOtter e466c06cad fix: add 60s timeout to all HEIF integration tests for CI
HEIF decoding via heif-convert is slow on CI runners (~15-30s per
image) and exceeds the default 30s Vitest timeout. Add explicit
60s timeouts to all 16 HEIF input tests across integration suite.
2026-05-01 14:50:03 +08:00
SnapOtter 64a8ab6944 chore: ignore major version bumps in dependabot for pip and docker
Dependabot was creating orphaned branches for risky major bumps
(Node 22->25, CUDA 12->13, Pillow 11->12, onnxruntime 1.20->1.25)
without opening PRs. These require manual evaluation, not auto-update.
Aligns pip and docker config with the npm ecosystem which already
ignores major bumps.
2026-05-01 14:16:32 +08:00
SnapOtter fa479dcee4 fix: resolve 3 pre-existing issues found during test coverage expansion
1. passport-photo 404 vs 501: add base route at /api/v1/tools/passport-photo
   that returns 501 FEATURE_NOT_INSTALLED when the AI bundle is missing,
   matching other AI tools. The /generate sub-route is Sharp-only (no
   sidecar) so it correctly skips the isToolInstalled guard.

2. AuthGuard analytics consent race: don't evaluate shouldShowConsent()
   until analyticsConfig has been fetched (guard on analyticsConfig !== null).
   Prevents redirect to /analytics-consent before config is loaded.

3. Fragile sidebar Settings selector: add openSettings(page) helper to
   E2E helpers that checks sidebar visibility with fallback to button role.
   Replace all 134 occurrences of page.locator("aside").getByText("Settings")
   across 18 test files.
2026-05-01 13:44:04 +08:00
SnapOtter 7ce0f7b431 Merge branch 'worktree-agent-a5c6c830' 2026-05-01 05:28:37 +08:00
SnapOtter 8e3e212146 Merge branch 'worktree-agent-ab98adb0' 2026-05-01 05:28:37 +08:00
SnapOtter d05337ad49 Merge branch 'worktree-agent-a8a8fe4d' 2026-05-01 05:28:37 +08:00
SnapOtter 1889f00554 test: comprehensive GUI settings E2E including API Keys tab and full RBAC coverage 2026-05-01 05:28:00 +08:00
SnapOtter ae241cec19 test: deepen GUI resilience, accessibility, and performance tests 2026-05-01 04:47:55 +08:00
SnapOtter 7342d9d8fd test: add tablet visual regression baselines and cross-browser tests 2026-05-01 04:17:19 +08:00
SnapOtter 43cac02331 Merge branch 'worktree-agent-a3a33bf5' 2026-05-01 04:13:56 +08:00
SnapOtter b5b66248f1 Merge branch 'worktree-agent-a062c389' 2026-05-01 04:13:56 +08:00
SnapOtter d40adb9789 Merge branch 'worktree-agent-a63c6a97' 2026-05-01 04:13:56 +08:00
SnapOtter e071c2868b test: comprehensive GUI E2E for all 47 tools including 13 AI tools 2026-05-01 04:12:57 +08:00
SnapOtter 241dedc31f test: deepen GUI navigation with mobile viewport, footer, responsive, and keyboard tests 2026-05-01 03:35:07 +08:00
SnapOtter b6ee96ed04 test: deepen GUI batch, pipeline, and file-carry E2E tests 2026-05-01 03:33:52 +08:00
SnapOtter 27a144e4d7 Merge branch 'worktree-agent-ae5b9f86' 2026-05-01 03:05:31 +08:00
SnapOtter 6762a4b367 Merge branch 'worktree-agent-aedfd941' 2026-05-01 03:05:31 +08:00
SnapOtter 548d2a30fe Merge branch 'worktree-agent-ac2de4e3' 2026-05-01 03:05:31 +08:00
SnapOtter b193a8321f Merge branch 'worktree-agent-a19378fc' 2026-05-01 03:05:31 +08:00
SnapOtter c9ed48b30e test: deepen integration tests for 8 complex tools (HEIC, large file, batch, edge cases) 2026-05-01 03:05:08 +08:00
SnapOtter e3c5279b92 test: expand E2E-Docker coverage to all 47 tools with auth-failure tests 2026-05-01 02:51:42 +08:00
SnapOtter b663a1fbab test: deepen integration tests for 11 medium tools (HEIC, large file, batch, edge cases) 2026-05-01 02:48:35 +08:00
SnapOtter 270f54950d test: deepen integration tests for 6 simpler tools (HEIC, large file, batch, edge cases) 2026-05-01 02:46:04 +08:00
SnapOtter b622245a35 Merge branch 'worktree-agent-a5f4fa03' 2026-05-01 02:30:04 +08:00
SnapOtter ea665915b4 Merge branch 'worktree-agent-a02ae816' 2026-05-01 02:30:01 +08:00
SnapOtter 3213d05e5c Merge branch 'worktree-agent-aa6874c3' 2026-05-01 02:29:58 +08:00
SnapOtter 41455f98bd test: add path traversal, null-byte, unicode, and concurrent request tests
New adversarial-security.test.ts covering 28 security-focused test cases:
- Path traversal attacks (7): Unix, Windows-style, URL-encoded, double-encoded, embedded
- Null byte injection (4): before extension, embedded, null-only, combined with traversal
- Extreme filename lengths (5): 1000-char, 5000-char, special-char-only, spaces, repeated dots
- Unicode filenames (8): Arabic RTL, Korean, Devanagari, complex emoji, RTLO char, ZWJ, tabs
- Concurrent request racing (4): 10 simultaneous with integrity check, batch+single isolation,
  10 across 5 tools, adversarial+valid mixed
- Server stability (1): post-barrage health verification

No real vulnerabilities found -- sanitizeFilename() in lib/filename.ts properly
handles all tested attack vectors via basename(), dot-dot stripping, and null
byte removal.
2026-05-01 02:29:20 +08:00
SnapOtter 88e6355642 test: add AI tool format matrix covering 13 tools x 17 formats 2026-05-01 02:26:52 +08:00
SnapOtter 7d915f7439 test: deepen AI bridge unit tests with dispatcher protocol and concurrency coverage 2026-05-01 02:25:57 +08:00
SnapOtter 0a0512a153 test: fill image-engine unit test branch coverage gaps 2026-05-01 02:19:56 +08:00
SnapOtter 710b580cee chore: add VS Code workspace config and improve CI infrastructure
Add .vscode/ with Biome formatter, Tailwind, Vitest, Playwright, and
Python debug configs. Extract shared pnpm/Node setup into a composite
GitHub Action and add Dependabot and dependency-review workflows.
2026-05-01 00:28:10 +08:00
SnapOtter d12b1c0fc6 fix: harden all AI tools against proxy timeouts and filename attacks
Convert all 9 AI tool routes (colorize, restore-photo, remove-background,
enhance-faces, blur-faces, red-eye-removal, erase-object, noise-removal,
upscale) to async 202 processing so none are vulnerable to proxy
connection timeouts.

Also fixes:
- Replace basename() with sanitizeFilename() in all AI tool routes
  (prevents double-extension attacks and adds length truncation)
- Add UUID format validation for clientJobId field
- Fix missing filename sanitization in noise-removal (was using raw
  user-supplied filename with zero sanitization)
- Remove em dash from error message in use-tool-processor
2026-05-01 00:11:03 +08:00
SnapOtter 4900d8a4fe fix: upscale times out behind Cloudflare Tunnel due to blocking HTTP request
The upscale route held the HTTP connection open for the full duration of
Python sidecar processing (30-300s). Behind proxies with connection
timeouts (Cloudflare Tunnel: 100s), this caused HTTP 524 errors.

The route now returns 202 Accepted immediately after upload validation
and processes in the background. The result (downloadUrl, sizes, etc.)
is delivered via the existing SSE progress channel. The frontend detects
the 202 and waits for the SSE completion event instead of reading the
XHR response body. A reconnect-safe completion store ensures results
survive brief SSE disconnects.

Closes #106
2026-04-30 23:43:55 +08:00
SnapOtter 9d8c3f4027 fix: GPU never used for inference despite healthy CUDA plumbing (#104)
Three interacting bugs prevented GPU inference from ever engaging:

1. onnx_providers() trusted torch.cuda without verifying onnxruntime
   actually has CUDAExecutionProvider -- silent CPU fallback
2. Dispatcher close handler counted normal MAX_REQUESTS exits as crashes,
   permanently disabling the dispatcher after routine restarts
3. Dispatcher was lazy-started on first AI request with a race condition
   that always missed it -- added initDispatcher() for eager startup

Closes #104
2026-04-30 19:27:56 +08:00
SnapOtter 42afa7c0bf fix: eagerly start AI dispatcher at boot and log actual GPU status
Replaces the misleading 'waiting for AI sidecar startup...' message that
never resolved. The dispatcher now starts during server init, and the
startup log shows the actual GPU detection result.
2026-04-30 18:49:52 +08:00
SnapOtter b344edf416 feat: add initDispatcher() for eager sidecar startup
The dispatcher was lazy-initialized on first AI request, but a race
condition meant the first call always missed it (dispatcherReady still
false) and fell through to cold per-request Python. initDispatcher()
starts the dispatcher eagerly and returns a Promise that resolves with
GPU status once ready (or after a timeout).
2026-04-30 18:48:15 +08:00
SnapOtter 6d5d0a3673 fix: do not count normal dispatcher exits as crashes
The close handler called recordCrash() unconditionally, even for exit
code 0 (normal MAX_REQUESTS restart). After 5 normal cycles within 60s
the dispatcher was permanently disabled. Now only non-zero exits count.
2026-04-30 18:45:55 +08:00
SnapOtter 67fa302376 fix: verify CUDAExecutionProvider in onnxruntime before returning CUDA providers
gpu.onnx_providers() trusted gpu_available() which returns True via
torch.cuda without checking whether onnxruntime actually has
CUDAExecutionProvider compiled in. When onnxruntime (CPU-only) is
installed, this caused silent fallback to CPU in every ONNX-based tool.

Now verifies onnxruntime.get_available_providers() directly and emits a
diagnostic warning when torch sees CUDA but onnxruntime does not.

Closes #104
2026-04-30 18:43:47 +08:00
SnapOtter d727429e9f fix: increase timeout for max-size QR generation test in CI
The 10000x10000 QR code generation exceeds the default 30s timeout on
GitHub Actions runners. Bump to 120s to accommodate slower CI hardware.
2026-04-30 17:05:34 +08:00
SnapOtter b00ef20667 fix: close SVG sanitization gap on upload routes and fix OCR/extension bugs
Security:
- Apply sanitizeSvg() to all file upload routes (files.ts, user-files.ts)
  preventing SSRF and script injection via SVG uploads to file library

Functional:
- Handle PaddleOCR-VL 1.5 markdown_texts output format in ocr.py
- Add empty-text fallback in OCR tier chain (ocr.ts) so higher tiers
  that return empty text fall back to the next tier automatically
- Fix SVG->PNG filename extension mismatch in tool-factory.ts so
  download endpoint serves correct Content-Type
- Report original upload size (not decoded size) in API response

Test infrastructure:
- Move Playwright auth state from test-results/ to .playwright/ to
  prevent mid-run cleanup deleting auth files
- Fix auth.setup.ts navigation race with waitForURL
- Fix gui-batch.spec.ts regex matching "Presets" instead of "reset"
- Fix pipeline-advanced.spec.ts crop bounds and resize assertions
- Broaden pipeline cleanup to include all E2E-prefixed pipelines
2026-04-30 16:11:33 +08:00
SnapOtter fc8b549d78 fix: gate captureException on user consent and fix HEIC PII scrubbing
captureException now checks isRequestOptedIn before forwarding errors
to Sentry, closing a gap where server errors leaked to an external
service even when no user had consented. The PII scrubbing regex is
also fixed: he[ic]f? failed to match .heic due to word-boundary
behavior and is replaced with hei[cf]? which correctly covers .heic,
.heif, and .hei.

Adds 88 new analytics tests across unit, integration, and e2e layers
proving PostHog/Sentry are never invoked when analytics is disabled or
users have not consented, plus full 7-day reminder lifecycle coverage.
2026-04-29 23:47:19 +08:00
SnapOtter 53343a0836 fix: wire up 7-day consent re-prompt in AuthGuard
The shouldShowConsent function in the shared package had the correct
logic for checking analyticsConsentRemindAt, but the AuthGuard never
used it. The inline check only redirected to the consent page when
both analyticsEnabled and analyticsConsentShownAt were null, which
is never true after "remind later" since shownAt gets set.

- Destructure analyticsConsentRemindAt from useAuth session
- Hydrate remindAt into the analytics store instead of hardcoding null
- Replace inline redirect check with shouldShowConsent from shared pkg
- Read analyticsConfig from the store inside AuthGuard for serverEnabled
2026-04-29 14:42:37 +08:00