- Vault: AES-256-GCM via Web Crypto API
- Key Derivation: PBKDF2 with 210,000 iterations + random 16-byte salt (legacy 100K auto-migrated)
- IV: Random 12-byte per encryption operation
- Password Hash: Salted SHA-256, format
base64(salt):base64(hash)
| Data | Storage | Encryption |
|---|---|---|
| Clipboard history | SQLite (qliplab.db) |
None (plaintext) |
| Snippets | SQLite | None |
| Vault items | SQLite (encrypted_data column) |
AES-256-GCM |
| Settings | Tauri Store (settings.json) |
None |
| Master password | Never stored (only salted hash) | SHA-256 + salt |
- CSP enabled in
tauri.conf.jsonwith restrictedconnect-src - Allowed external connections:
- Cloudflare Worker issue reporter endpoint (
qliplab-api.omercelikdev.workers.dev) - jsdelivr CDN (Monaco editor)
- Cloudflare Worker issue reporter endpoint (
- No telemetry — no background data collection
- No clipboard content ever leaves the device — there is no cloud component
- Report endpoint is proxied through the Cloudflare Worker (no direct GitHub token exposure)
Enabled via Entitlements.plist:
| Entitlement | Purpose |
|---|---|
com.apple.security.app-sandbox |
App sandboxing |
com.apple.security.network.client |
Error reporting, update check |
com.apple.security.automation.apple-events |
Paste simulation (Cmd+V) |
com.apple.security.files.user-selected.read-write |
Export/import data |
com.apple.security.cs.allow-unsigned-executable-memory |
WebView/Tauri runtime |
Vault unlock attempts are rate-limited with exponential backoff:
| Failed Attempts | Delay |
|---|---|
| 3 | 3 seconds |
| 5 | 10 seconds |
| 7 | 30 seconds |
| 10+ | 60 seconds |
- QlipLab is licensed under Apache-2.0 (see
LICENSE) - No first-launch EULA gate; a viewable Terms/disclaimer is available in
Settings → Terms of Use (
EulaViewerDialog) - Nothing is recorded to any server
- Opt-in only (default OFF)
- Shown on first launch
- Can be toggled anytime in Settings
- Rate limited: 10/hour, 50/day
- No clipboard/vault/personal data sent
- Accessible from Settings → "Privacy Policy"
- Describes data handling, storage, third-party services
Required since Spring 2024. Declares:
- Required Reason APIs used by the app
- Tracking declaration (NO tracking)
- Data collection types
| Key | Description | Required For |
|---|---|---|
NSAppleEventsUsageDescription |
Paste simulation | Accessibility permission dialog |
When submitting to App Store Connect, declare:
| Data Type | Collected | Linked to User | Tracking |
|---|---|---|---|
| Crash Data | Yes (opt-in) | No | No |
| Performance Data | No | — | — |
| Clipboard Data | Local only | No | No |
| Identifiers | No | — | — |
| Usage Data | No | — | — |
| Contact Info | No | — | — |
All external reporting is disabled in development:
if (import.meta.env.DEV) return; // skip in developmentApplied to:
errorReporter.ts— auto error reportsfeedbackStore.ts— manual issue reports
- AES-256-GCM for vault encryption
- PBKDF2 with 210K iterations for key derivation (legacy 100K auto-migrated)
- Random salt (16 bytes) and IV (12 bytes) per operation
- Salted password hashing (not plain SHA-256)
- No spread operator overflow on large arrays
- Constant-time password comparison (prevents timing attacks)
- Vault form validation (Luhn, IBAN, SWIFT, email)
- Input length limits on all form fields
- SQL parameterized queries (no injection)
- CSP prevents XSS
- App Sandbox enabled
- Vault brute-force protection
- Vault auto-lock timer
- Sensitive data detection (API keys, passwords, tokens, PEM keys, financial data)
- No clipboard content sent to external servers, ever
- Vault data encrypted at rest
- Sensitive items blurred in the history list until hovered
- Export/import uses file picker (sandboxed)
- Stack trace sanitization in error reports (no absolute paths)
- Enhanced sensitive pattern detection (JSON/YAML/env formats, Slack/SendGrid/GitHub OAuth tokens)
- CSP with restricted connect-src
- HTTPS only for all external connections
- No direct GitHub token in client
- No consent/agreement storage of any kind (nothing to consent to)
- Apache-2.0 licensed; Terms/disclaimer viewable in-app (no gate)
- Error reporting opt-in (not opt-out)
- Privacy Policy accessible in-app
- PrivacyInfo.xcprivacy for App Store
| # | Finding | Severity | Status |
|---|---|---|---|
| 1 | Password comparison used === (timing attack vector) |
High | Fixed — timingSafeEqual() in encryption.ts |
| 2 | Stack traces exposed absolute file paths in error reports | Medium | Fixed — sanitizeStack() in errorReporter.ts |
| 3 | Error hash used truncated base64 (collision risk) | Low | Fixed — proper numeric hash in errorReporter.ts |
| 4 | Sensitive patterns missed JSON/YAML/env formats | Medium | Fixed — quoted key support in formatDetector.ts |
| 5 | Missing detection for Slack, SendGrid, GitHub OAuth tokens | Medium | Fixed — added xox*, SG.*, gho_* patterns |
| 6 | Missing EC/OPENSSH private key detection | Low | Fixed — extended PEM regex in formatDetector.ts |
| 7 | Missing database URL / connection string detection | Medium | Fixed — added patterns in formatDetector.ts |
- PBKDF2 210K iterations: NIST recommends 210K+ for SHA-256. Current level meets the standard. Higher iterations (600K+) would degrade UX on mobile/low-power devices.
- Clipboard history in plaintext: By design — sensitive items are flagged and users are warned. Vault exists for truly sensitive data.
- Local SQLite without encryption: Covered by macOS App Sandbox + file system permissions. Full-database encryption (SQLCipher) would add complexity without meaningful benefit for local-only data.