Files
cg_api_secure-webshare/agent1_batch3.md

4.2 KiB
Raw Blame History

Server-Side Password Authentication Flow Assessment (Batch 3)

File inspected: crates/cgcx-server/src/main.rs

Flow Verification

1. get_metadata handler (/api/content/:cxid)

  • Lines ~496568
  • Sequence:
    1. Content status check (Deleted/Blacklisted) → 404 NotFound
    2. Max-views check (content.view_count >= max) → 410 Gone
    3. Password validation via password_from_request(&headers, query.sc.as_deref(), ...)401 Unauthorized if invalid
    4. Returns metadata JSON if all checks pass
  • View increment: None. Metadata requests do not consume auto-destroy views.
  • Conclusion: Password check is enforced and returns 401 on failure.

2. serve_file handler (/api/content/:cxid/file/:file_idx)

  • Lines ~700890
  • Sequence:
    1. Content status check → 404
    2. Max-views check → 410
    3. Password validation via password_from_request401 if invalid
    4. Download permission check → 403 Forbidden if ?download=true but not allowed
    5. File lookup, path-traversal validation, ETag conditional check (304)
    6. Range header parsing
    7. View increment at line ~825: repo.increment_views(&content_id).await?
    8. If new_views >= max, spawns background auto-delete task after 30s
    9. Streams decrypted file body
  • Conclusion: View increment happens after password validation and all other guards. Unauthorized requests cannot consume views.

3. password_from_request helper

  • Lines ~436475
  • Validates sc query parameter using Argon2 (PasswordHash::new + Argon2::default().verify_password).
  • Falls back to cgcx_pw cookie verified with HMAC-SHA256 and subtle::ConstantTimeEq.
  • Returns None on any mismatch, causing the caller to return 401.
  • Conclusion: Argon2 verification is present and constant-time.

4. serve_raw_file handler (/api/content/:cxid/file/:file_idx/raw)

  • Lines ~9201020
  • Sequence mirrors serve_file for status, max-views, and password checks.
  • View increment: None. This endpoint never increments view_count.

Edge Cases & Concerns

# Edge Case Impact Risk
1 serve_raw_file skips view increment Requests to the /raw endpoint do not consume auto-destroy views. A user could repeatedly preview raw text without triggering deletion. Medium — bypasses view-count enforcement for text content previews.
2 Zero-size files in serve_file return early The zero-size branch (line ~751) returns a response before the increment block, so zero-byte files never consume a view. Low — niche, but technically bypasses max-views for empty files.
3 Range/conditional/HEAD requests skip increment serve_file only increments when !is_range && !is_conditional && !is_head. Video/audio seeking (range requests), cache revalidation (If-None-Match), and HEAD probes do not count as views. Low — intentional for UX, but means views are under-counted.
4 Max-views checked before password validation In all three handlers, if view_count >= max, a request with a wrong password receives 410 Gone instead of 401 Unauthorized. This leaks that the content existed and exhausted its views without requiring the password. Low — information disclosure about content lifecycle.
5 TOCTOU race on view increment content.view_count is read in repo.get() and incremented later in a separate statement. Under concurrent requests, two clients could both pass the initial view_count < max check and both increment, causing view_count to exceed max. Both requests are still served. Low — SQLite serializes writes via conn.lock(), but the read-write gap still allows one extra view.

Conclusion

  • Password authentication flow is correct. Wrong passwords receive 401 and do not increment view counts.
  • No server changes are required for Batch 3. The frontend password fix (if any) is independent of server behavior.
  • The identified edge cases are pre-existing behaviors. None are blockers for Batch 3, but items 1 (serve_raw_file bypass) and 5 (TOCTOU race) may be worth addressing in a future hardening pass if auto-destroy accuracy is critical.