Authenticated users can tag pastes with a human-readable label
via X-Display-Name header. Supports create, update, remove, and
listing. Max 128 chars, control characters rejected.
get_pow_secret() generated a random secret per process, so challenges
signed by worker A failed verification on worker B (~90% failure rate
with 2 workers). Persist a file-backed secret to data/.pow_secret
using O_EXCL for atomic creation. FLASKPASTE_POW_SECRET env var
still takes priority when configured.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Health check endpoint was being rate-limited (60/hour), causing
container health checks (every 30s = 120/hour) to fail with 429.
Uses flask-limiter's request_filter to bypass rate limiting for
the health endpoint, supporting URL_PREFIX configuration.
Remove magic byte detection in favor of simple UTF-8 validation:
- text/plain for valid UTF-8 content
- application/octet-stream for binary data
Security maintained via headers (X-Content-Type-Options: nosniff, CSP).
Magic signatures preserved as comments for future reference.
Disabled test files:
- test_mime_detection.py.disabled (magic-dependent tests)
- test_polyglot.py.disabled (polyglot format tests)
For full MIME detection, consider using the `filetype` library.
ENUM-002: Lookup rate limit now respects LOOKUP_RATE_LIMIT_MAX_ENTRIES
(default 10000) to prevent memory exhaustion from unique IP flood.
Eviction strategy: expired entries first, then oldest by last request.
CRYPTO-001: Certificate serial collision detection
- Add _generate_unique_serial() helper for database-backed PKI
- Add _generate_unique_serial() method for in-memory PKI class
- Check database for existing serial before certificate issuance
- Retry with new random serial if collision detected (max 5 attempts)
TIMING-001: Constant-time database lookups for sensitive queries
- Add dummy PBKDF2 verification when paste not found
- Prevents timing-based enumeration (attackers can't distinguish
'not found' from 'wrong password' by measuring response time)
HASH-001: Add threading lock to content hash deduplication
- Prevents race condition between SELECT and UPDATE
- Ensures accurate dedup counting under concurrent load
ENUM-001: Add rate limiting to paste lookups
- Separate rate limiter for GET/HEAD on paste endpoints
- Default 60 requests/minute per IP (configurable)
- Prevents brute-force paste ID enumeration attacks
FLOOD-001: Cap anti-flood request list at configurable max entries
- Add ANTIFLOOD_MAX_ENTRIES config (default 10000)
- Prune oldest entries when limit exceeded
CLI-002: Explicitly set SSL hostname verification
- Add ctx.check_hostname = True and ctx.verify_mode = CERT_REQUIRED
- Defense in depth (create_default_context sets these by default)
CLI-003: Warn on insecure config file permissions
- Check if config file is world-readable
- Print warning to stderr if permissions too open
AUDIT-001: Already implemented - query has LIMIT/OFFSET with 500 max
PROXY-001: Add startup warning when TRUSTED_PROXY_SECRET empty in production
- validate_security_config() checks for missing proxy secret
- Additional warning when PKI enabled without proxy secret
- Tests for security configuration validation
BURN-001: HEAD requests now trigger burn-after-read deletion
- Prevents attacker from probing paste existence before retrieval
- Updated test to verify new behavior
RATE-001: Add RATE_LIMIT_MAX_ENTRIES to cap memory usage
- Default 10000 unique IPs tracked
- Prunes oldest entries when limit exceeded
- Protects against memory exhaustion DoS
Test count: 284 -> 291 (7 new security tests)
Add is_admin() helper to check if current user is admin.
Update DELETE /<id> to allow admin to delete any paste.
Update GET /pastes to support ?all=1 for admin to list all pastes.
Admin view includes owner fingerprint in paste metadata.
Auto-detect first certificate issuance and grant admin flag.
Add is_admin column to issued_certificates table.
Add is_admin_certificate() helper function.
Include is_admin in /pki/issue response and X-Is-Admin header in registration.