If you find a security vulnerability, please report it via a private GitHub Vulnerability Report or by contacting the maintainers directly.
-
Secrets Management: Environment variables, including API keys and credentials, are supplied at build or run time and are not committed. The mobile clients read the feedback-service key from
BuildConfigon Android (set inlocal.propertiesor the environment) and from theSYNC_API_KEYInfo.plist key on iOS; both treat an absent key as "sync disabled" rather than an error.This line described an intention rather than a fact until 2026-08-16. The feedback-service key was a literal in
SyncService.swiftandSyncWorker.ktfrom 2026-04-11, in this public repository, next to the endpoint it authenticates against. It has been removed from the source, but it remains in the git history and in every build shipped in that window, so that key must be rotated server-side — removing it here does not un-publish it.A
gitleaksjob now runs on every push and scans the full history, so this is enforced rather than asserted.apps/web/package.jsonhad defined ascan:secretsscript since before the incident; nothing ran it, because the repository had no CI. -
Service Security: The Feedback service is protected by API key authentication. Internal documentation (Swagger) is also restricted to authenticated requests.
-
Web Upload Signing: The web app does not use the Feedback service. It asks
apps/web's/api/sync/signfor a 15-minute signed PUT URL into the dataset bucket. That endpoint has no authentication: it accepts only the exact originhttps://ocr.mondevhub.com(pluslocalhostand127.0.0.1in development), and refuses a request with noOriginheader. An Origin check stops other websites, not a client that forges the header, so a rate limit at the edge (a Cloudflare rate-limiting rule on/api/sync/sign) is recommended for any deployment. -
Data Protection: Uploads are stored in Cloudflare R2 with access controls. Objects are organized using unique identifiers and date-based partitioning to prevent enumeration.
Each service implements standard security checks:
- Content Verification: The Feedback service verifies files using MIME-type sniffing (magic numbers) to ensure they match the expected format. Web uploads go directly to storage and are not sniffed:
/api/sync/signchecks the declared type against the same five types (JPEG, PNG, WebP, PDF, plain text) and signs it into the URL, so the upload must carry thatContent-Type. - Payload Limits: Uploads are capped at 20 MiB. The Feedback service limits the request body;
/api/sync/signrefuses a declared size over 20 MiB and signs the size into the URL, so the upload must carry exactly thatContent-Length. - Sanitization: External inputs are sanitized before being used in storage operations.