From 74d7f34d34da9853961777ea0a437dea0aa42d16 Mon Sep 17 00:00:00 2001 From: SnapOtter Date: Fri, 15 May 2026 22:56:42 +0800 Subject: [PATCH] chore: remove local-only files from git tracking Untrack CLAUDE.md, QA reports, security audit report, and superpowers docs. These are internal working documents that should never be pushed to GitHub. Updated .gitignore to ensure they stay local. --- .gitignore | 2 + CLAUDE.md | 162 - SECURITY_AUDIT_REPORT.md | 288 -- docs/QA_REPORT_2026-05-15_22-43-06.md | 168 - .../plans/2026-05-08-remotion-promo-videos.md | 3264 ----------------- ...26-05-13-restore-photo-quality-overhaul.md | 1099 ------ ...2026-05-08-remotion-promo-videos-design.md | 736 ---- ...3-restore-photo-quality-overhaul-design.md | 182 - 8 files changed, 2 insertions(+), 5899 deletions(-) delete mode 100644 CLAUDE.md delete mode 100644 SECURITY_AUDIT_REPORT.md delete mode 100644 docs/QA_REPORT_2026-05-15_22-43-06.md delete mode 100644 docs/superpowers/plans/2026-05-08-remotion-promo-videos.md delete mode 100644 docs/superpowers/plans/2026-05-13-restore-photo-quality-overhaul.md delete mode 100644 docs/superpowers/specs/2026-05-08-remotion-promo-videos-design.md delete mode 100644 docs/superpowers/specs/2026-05-13-restore-photo-quality-overhaul-design.md diff --git a/.gitignore b/.gitignore index b7205fb0..71a9d48f 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,8 @@ stirling-pdf-*.png settings-*.png layout-*.png audit_report.md +apps/docs/SECURITY_AUDIT_REPORT.md +docs/QA_REPORT*.md .worktrees/ .release-version .models diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 31ab1058..00000000 --- a/CLAUDE.md +++ /dev/null @@ -1,162 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## What Is SnapOtter - -Open-source, self-hostable image manipulation suite (51 tools). Single Docker container, no external services. Dual-licensed AGPLv3 / commercial. - -## Monorepo Layout - -``` -apps/web/ # Main React SPA (port 1349) -apps/api/ # Fastify API server (port 13490) -apps/landing/ # Marketing site (Vite + React, deployed to Cloudflare Pages) -apps/docs/ # VitePress documentation site (deployed to Cloudflare Pages) -packages/shared/ # Constants, types, i18n, permissions -- consumed by all apps -packages/image-engine/ # Sharp-based image processing pipeline -packages/ai/ # Python sidecar bridge for ML models -``` - -## Tech Stack - -| Layer | Technology | -|-------|-----------| -| Frontend | React 19, Vite 6, Tailwind CSS 4, Zustand, react-router-dom v7 | -| Backend | Fastify 5, tsx (no compile step in dev), Sharp | -| Database | SQLite via Drizzle ORM (better-sqlite3) | -| AI/ML | Python sidecar (rembg, RealESRGAN, PaddleOCR, MediaPipe, LaMa) | -| Docs | VitePress | -| Testing | Vitest (unit/integration), Playwright (e2e) | -| CI/CD | GitHub Actions, semantic-release, Docker multi-arch | -| Linting | Biome (format + lint in one pass) | - -## Commands - -```bash -pnpm dev # Start all dev servers (web on :1349, api on :13490) -pnpm build # Build all workspaces (turbo, packages first) -pnpm typecheck # TypeScript check across monorepo -pnpm lint # Biome lint + format check -pnpm lint:fix # Biome auto-fix -pnpm test # All Vitest tests (unit + integration) -pnpm test:unit # Unit tests only -pnpm test:integration # Integration tests only (full API) -pnpm test:e2e # Playwright e2e tests (main app) -pnpm test:e2e:landing # Playwright e2e tests (landing site) -pnpm test:e2e:docs # Playwright e2e tests (docs site) -pnpm test:coverage # Tests with V8 coverage report - -# Run a single test file -pnpm vitest run tests/unit/my-test.test.ts -pnpm vitest run tests/integration/my-test.test.ts - -# Run a single e2e spec -pnpm playwright test tests/e2e/my-test.spec.ts - -# Database migrations (run from apps/api/) -cd apps/api && npx drizzle-kit generate # Generate from schema changes -cd apps/api && npx drizzle-kit migrate # Apply pending migrations -``` - -## Key Conventions - -- **Simplicity over complexity** -- do not over-engineer -- **Double quotes**, **semicolons**, **2-space indent** (enforced by Biome) -- **ES modules** in all workspaces (`"type": "module"`) -- Conventional commits for semantic-release (`feat:`, `fix:`, `refactor:`, `docs:`, `test:`, `chore:`) -- Zod for all API input validation -- i18n strings in `packages/shared/src/i18n/en.ts` (reference locale, ~1500 keys) -- All user-facing strings use `useTranslation()` hook from `apps/web/src/contexts/i18n-context.tsx` -- Use `t.section.key` for string references, `format(t.key, { var })` for interpolation, `plural(n, one, other)` for pluralization -- Tailwind uses logical properties for RTL support (`ms-` not `ml-`, `text-start` not `text-left`) -- 21 supported languages. Adding a new locale: create `packages/shared/src/i18n/.ts` typed as `TranslationKeys`, add to `SUPPORTED_LOCALES` in `index.ts` - -## Architecture: How a Tool Works - -A "tool" is defined in three places that share the same `toolId` string: - -1. **Shared metadata** (`packages/shared/src/constants.ts`) -- `TOOLS[]` array with id, name, description, category, icon, route. Single source of truth for tool metadata consumed by both frontend and backend. - -2. **API route** (`apps/api/src/routes/tools/.ts`) -- Processing logic. Most tools use the `createToolRoute` factory (`apps/api/src/routes/tool-factory.ts`), which handles multipart parsing, file validation, format decoding (HEIC/RAW/PSD), SVG sanitization, EXIF auto-orientation, Zod settings validation, output saving, preview generation, file versioning, and analytics. A simple tool is ~30 lines: a Zod schema + a process function that calls `@snapotter/image-engine`. Routes are registered in `apps/api/src/routes/tools/index.ts`. - -3. **Frontend settings** (`apps/web/src/components/tools/-settings.tsx`) -- UI component for the tool's settings panel. Registered in `apps/web/src/lib/tool-registry.tsx` with a `displayMode` (side-by-side, before-after, live-preview, interactive-crop, interactive-eraser, no-dropzone, custom-results). - -### Adding a New Tool - -1. Add tool definition to `TOOLS[]` in `packages/shared/src/constants.ts` -2. Create `apps/api/src/routes/tools/.ts` -- export a `register(app)` function that calls `createToolRoute(app, { toolId, settingsSchema, process })` -3. Add to the registration array in `apps/api/src/routes/tools/index.ts` -4. Create `apps/web/src/components/tools/-settings.tsx` -5. Add lazy import + registry entry in `apps/web/src/lib/tool-registry.tsx` -6. Add i18n strings in `packages/shared/src/i18n/en.ts` - -### Request Lifecycle - -**Standard tools**: Frontend sends `POST /api/v1/tools/:toolId` (multipart with file + JSON settings) via XHR with upload progress. The `createToolRoute` factory processes and returns `{jobId, downloadUrl, originalSize, processedSize}`. - -**AI tools**: Do NOT use `createToolRoute` for their HTTP route. They return `202 Accepted` immediately, process asynchronously via the Python sidecar, and stream progress via SSE (`EventSource` on `/api/v1/jobs/:jobId/progress`). They still register a sync wrapper via `registerToolProcessFn()` for pipeline/batch reuse. - -### Pipeline System - -Pipelines chain tools: each step's output buffer feeds the next step's input. The tool registry (`toolRegistry` Map) enables pipelines and batch processing to call any registered tool's process function without duplicating logic. CRUD stored in `pipelines` DB table as JSON. Batch processing uses `p-queue` concurrency and returns a ZIP via `archiver`. - -## AI Sidecar (packages/ai/) - -Two-tier Python execution: - -1. **Persistent dispatcher** (primary): Long-lived Python process (`packages/ai/python/dispatcher.py`) communicating via JSON over stdio. Node sends `{id, script, args}` on stdin, Python responds `{id, stdout, exitCode}` on stdout. Progress events stream on stderr as `{progress, stage}`. Pre-imports heavy ML libraries at startup to eliminate cold-start latency. Auto-restarts after 50 requests to prevent memory leaks. - -2. **Per-request fallback**: Fresh Python process per request if dispatcher is unavailable. Exponential backoff with crash recovery (5 crashes in 60s = permanent disable). - -AI tools require model bundles defined in `packages/shared/src/features.ts` (`FEATURE_BUNDLES`). The API checks `isToolInstalled()` before processing; the frontend shows an install prompt if not installed. - -## Image Engine (packages/image-engine/) - -`processImage(input, operations[], outputFormat?)` pipes a buffer through named operations. Each operation is a pure function `(Sharp, options) => Promise`. The `OPERATION_MAP` in `engine.ts` dispatches by string key. Individual operations are also exported for direct use by API routes. - -## Auth and Permissions - -Session-based auth with scrypt password hashing. Three built-in roles (`admin`, `editor`, `user`) with 14 granular permissions defined in `packages/shared/src/permissions.ts`. Custom roles stored in `roles` table. API keys (prefixed `si_`) carry optional scoped permissions that intersect with user role permissions. Auth can be disabled entirely (synthetic anonymous user with `user` role). - -## Frontend State - -14 Zustand stores in `apps/web/src/stores/`. The central one is `file-store.ts` managing `FileEntry[]` with blob URLs, processing status, and batch ZIP. Key hooks: `use-tool-processor.ts` (single-file XHR + batch + SSE progress) and `use-pipeline-processor.ts`. - -All page components are `lazy()`-loaded. The key route is `/:toolId` rendering `ToolPage` (`apps/web/src/pages/tool-page.tsx`), which looks up metadata from shared constants and the frontend tool registry. - -## Database - -SQLite via Drizzle ORM. Schema: `apps/api/src/db/schema.ts`. Migrations in `apps/api/drizzle/`. - -Tables: users, teams, sessions, settings (key-value), jobs, apiKeys, pipelines, auditLog, roles, userFiles. - -## Testing - -- `tests/unit/` -- Vitest unit tests -- `tests/integration/` -- Vitest integration tests (one per tool/feature, full API) -- `tests/e2e/` -- Playwright specs (Chromium primary, Firefox/WebKit for cross-browser) -- `tests/fixtures/` -- Small test images in various formats - -Vitest uses single-fork pool (shared SQLite connection), 30s timeouts, and injects test env vars. Playwright auth setup project logs in and saves storage state; spins up both dev servers with a fresh test DB. - -## Pre-commit Hooks - -Husky + lint-staged runs `biome check --write` on staged `*.{ts,tsx,js,jsx,json}` files before every commit. If a commit fails, fix the lint issues rather than bypassing the hook. - -## Do Not Modify Config Files - -Biome, TypeScript, and editor config files are protected by hooks. Fix the code to satisfy the linter/compiler, not the other way around. This prevents a common AI failure mode where rules get weakened instead of code getting fixed. - -## Model Routing for Subagents - -Always use **Opus 4.7, Max Effort, 1M context** for every agent and subagent. No exceptions. No model downgrading. - -## Strategic Compaction - -When context gets large, compact at logical phase boundaries: - -- **Good times to compact**: After research and before planning. After debugging and before implementing the fix. After completing a major feature. -- **Bad times to compact**: Mid-implementation. While actively debugging. During a multi-step refactor. -- **Survives compaction**: This CLAUDE.md, active tasks, git state, memory files -- **Lost on compaction**: Intermediate reasoning, file contents previously read, conversation flow diff --git a/SECURITY_AUDIT_REPORT.md b/SECURITY_AUDIT_REPORT.md deleted file mode 100644 index 8c6b54da..00000000 --- a/SECURITY_AUDIT_REPORT.md +++ /dev/null @@ -1,288 +0,0 @@ -# SnapOtter Security Audit Report - -**Date:** 2026-05-13 -**Version:** 1.16.0 -**Commit:** bc0cac4 (baseline) -**Auditor:** Claude (Principal Application Security Engineer) -**Scope:** Full codebase + production Docker container - -## Executive Summary - -- Total findings: 45 -- CRITICAL: 1 | HIGH: 10 | MEDIUM: 17 | LOW: 12 | INFO: 5 -- Fixed in this audit: 39 -- Deferred: 6 (with justification) -- Security tests added: 104 unit + 7 integration = 111 total -- SVG attack payload fixtures: 11 - -## Baseline Penetration Test - -Tested against production Docker container. 28 of 30 OWASP attack vectors blocked at baseline. - -| # | Attack | Baseline | After Hardening | -|---|--------|----------|-----------------| -| 10.1 | Path traversal (download) | PASS | PASS | -| 10.2 | Path traversal (upload filename) | PASS | PASS | -| 10.3 | IDOR on user files | PASS | PASS | -| 10.4 | Unauthenticated tool access | PASS | PASS | -| 10.5 | Privilege escalation via registration | PASS | PASS | -| 10.6 | Privilege escalation via self-update | PASS | PASS | -| 10.7 | Session token entropy | PASS | PASS | -| 10.8 | Password hash strength | PASS | PASS | -| 10.9 | SQL injection (login) | PASS | PASS | -| 10.10 | SQL injection (search) | PASS | PASS | -| 10.11 | Command injection (filename) | PASS | PASS | -| 10.12 | Header injection (CRLF) | PASS | PASS | -| 10.13 | Brute force login | FAIL | PASS | -| 10.14 | Account enumeration | PASS | PASS | -| 10.15 | Default credentials | PASS | PASS | -| 10.16 | Debug endpoints | PASS | PASS | -| 10.17 | Directory listing | PASS | PASS | -| 10.19 | Session fixation | PASS | PASS | -| 10.20 | Token reuse after logout | PASS | PASS | -| 10.21 | API key after revocation | PASS | PASS | -| 10.22 | Malicious file upload | PASS | PASS | -| 10.23 | Polyglot file | PASS | PASS | -| 10.24 | Audit log completeness | PASS | PASS | -| 10.25 | No sensitive data in logs | FAIL | PASS | -| 10.26 | SSRF cloud metadata | PASS | PASS | -| 10.27 | SSRF localhost | PASS | PASS | -| 10.28 | SSRF IP encoding bypasses | PASS | PASS | -| 10.29 | SVG XSS (script) | PASS | PASS | -| 10.30 | SVG XXE | PASS | PASS | -| 10.31 | SVG SSRF | PASS | PASS | -| 10.32 | SVG event handler | PASS | PASS | - -## Findings Detail - -### [CRITICAL] C1: drizzle-orm SQL Injection CVE - -- **File:** package.json -- **CWE:** CWE-89 -- **Status:** Fixed -- **Fix:** Updated drizzle-orm 0.38.4 to 0.45.2 - -### [HIGH] H1: Login Rate Limit Default Too High - -- **File:** apps/api/src/lib/env.ts:42 -- **CWE:** CWE-307 -- **Status:** Fixed -- **Impact:** 500 login attempts/min allowed brute force at 720K/day -- **Fix:** Default changed to 30/min - -### [HIGH] H2: Global Rate Limit Disabled - -- **File:** apps/api/src/lib/env.ts:23, apps/api/src/index.ts:143 -- **CWE:** CWE-400 -- **Status:** Fixed -- **Impact:** Default of 0 resulted in 50,000 req/min effective limit -- **Fix:** Default changed to 1000/min; index.ts uses `Math.max(env.RATE_LIMIT_PER_MIN, 1)` - -### [HIGH] H3: SVG Sanitizer Missing Protections - -- **File:** apps/api/src/lib/svg-sanitize.ts -- **CWE:** CWE-79, CWE-611 -- **Status:** Fixed -- **Impact:** CDATA bypass, entity-encoded javascript:, animation-based injection -- **Fix:** Added CDATA stripping, XML entity decoding, ``/``/`