Deliberation: technical — GPT-5.3-Codex
Date: 2026-03-17 23:44
Model: GPT-5.3-Codex
Type: technical
Artifact: ## Cloud Upload v1 — Full Deliverable ### 1. Server-side: PUT /api/upload (Common Lisp, add to serv
Architectural / Code Pattern Review
1) Common Lisp upload handler (Woo/Clack pattern)
:raw-body stream read loop is structurally correct for raw PUT uploads, if myway:*env* actually exposes a binary stream there [UNVERIFIED].
- Header access via
(gethash ... headers) is only correct if :headers is a hash table [UNVERIFIED]. In many Clack stacks, headers are alist/plist-like.
- Returning
200 for "Upload not configured" is wrong. Use 5xx (503 preferred) to avoid false client success.
content-type equality check is too strict. Real clients often send audio/mpeg; charset=binary. Parse media type before matching.
- Size enforcement is incomplete:
- If
Content-Length missing/invalid, limit is bypassed.
- No streaming byte counter; attacker can exhaust disk.
parse-integer :junk-allowed t is unsafe ("123abc" parses as 123).
- Mid-stream interruption edge case is not handled safely:
- Partial file may remain on disk if read/write errors occur.
- Use temp file + atomic rename only on success; delete temp on error/cancel.
- Filename collision claim is incorrect: second-level timestamp can collide under concurrency.
:if-exists :supersede can overwrite. Use UUID/random suffix.
sanitize-filename is insufficient:
- Does not remove control chars,
: (macOS path concerns), or enforce max length.
- Should normalize to strict allowlist, e.g.
[A-Za-z0-9._-], truncate, and reject empty.
bt:with-lock-held (*rescan-lock*) around full rescan + save-db can become a throughput bottleneck; consider narrower critical section or async indexing queue.
2) Swift concurrency / Sendable
@MainActor final class ... @unchecked Sendable is a red flag.
@unchecked Sendable is likely unnecessary and weakens guarantees. Remove unless compiler requires it for a proven-safe cross-actor path.
URLSessionTaskDelegate callback is nonisolated, which is correct, and hopping via Task { @MainActor ... } is safe.
- Cancellation behavior is ambiguous [CHECK THIS]:
- You cancel wrapper
Task, but no explicit handle to underlying URLSessionTask.
- Usually propagated, but if you need deterministic cancel semantics, use
uploadTask(with:fromFile:) and store/cancel the task directly.
- Progress callback can race with
cancel()/dismiss() and repopulate progress after reset. Gate updates with an upload token/ID.
- URL construction by string concatenation is brittle. Use
URLComponents or appendingPathComponent("api/upload").
X-Filename should be sanitized client-side too (strip CR/LF, control chars) before setting header.
Data model / API usage
- Error model is inconsistent: server returns
+400+ for unsupported type (likely generic) [UNVERIFIED]; client expects meaningful message. Return explicit JSON error for each reject path.
- Server does not validate body presence; zero-byte upload can pass and trigger rescan.
- Consider idempotency: repeated upload of same file currently creates duplicates unless rescan dedups [UNVERIFIED].
Security findings
- High risk: unbounded upload without strict streaming cap.
- Medium risk: insufficient filename sanitization (control chars, long names, hidden files).
- Medium risk: possible header/value trust issues with
X-Filename.
- Auth relies on
with-user [UNVERIFIED]; ensure endpoint cannot be hit anonymously.
Hallucination Detection on artifact claims
- “Raw body PUT matches existing server patterns” → [UNVERIFIED]
- “Task-based cancellation” fully cancels network transfer → [CHECK THIS]
- “
bt:with-lock-held avoids racing with background rescanner” → [UNVERIFIED] (depends on all codepaths using same lock)
- “unix-timestamp-sanitized-filename prevents collisions” → [CHECK THIS] (does not under same-second collisions)
- “strips
/ and \ (no path traversal)” → partially true, but sanitization remains incomplete; not sufficient as a full security claim.
Session State
**Blocking findings**:
- No enforced streaming max size when `Content-Length` is missing/invalid (disk exhaustion risk).
- Partial-file handling is unsafe (no temp file + cleanup on interrupted upload).
- Incorrect success status (`200`) for server misconfiguration.
- Filename collision risk due second-based naming + `:supersede`.
**Advisory findings**:
- Parse `Content-Type` robustly (handle parameters).
- Strengthen filename sanitization (allowlist, truncate, control-char strip).
- Remove/justify `@unchecked Sendable`.
- Use deterministic upload task cancellation and progress tokening.
- Replace URL string concatenation with path-safe URL APIs.
- Revisit coarse rescan lock scope for performance.
**Decisions**:
- Recommend temp-file write + byte-count enforcement + atomic rename as required server pattern.
- Recommend explicit structured error responses for each reject path.
**Open Questions**:
- Is `:headers` truly a hash table in this MyWay/Clack setup?
- Does `with-user` guarantee auth on this route?
- Does background rescanner always honor `*rescan-lock*`?
- Does `rescan` deduplicate by content/hash or path only?
**Key Entities**:
`upload-file`, `sanitize-filename`, `*max-upload-size*`, `+allowed-upload-types+`, `myway:*env*` **[UNVERIFIED]**, `:raw-body`, `*rescan-lock*` **[UNVERIFIED]**, `rescan`, `save-db`, `UploadService`, `URLSessionTaskDelegate`, `performUpload`, `X-Filename`.