Skip to content

Core sync: attachment Content-Type — three-source resolution, stored mimeType, safe-list forcing (core #65, #66) #37

Description

@cuibonobo

Server side of haverstack/core#65 (deterministic mimeType: first-recorded wins, conflicts rejected) and haverstack/core#66 (dangerous-type forcing applies to the result from any source, safe-list preferred, nosniff mandatory).

Current divergences in src/routes/attachments.ts

  1. The stored-metadata source is not implemented at all. Spec §Attachments: with no query params, "the server queries the _attachment@1 record: the stored mimeType becomes Content-Type, and the filename is taken from the requester's own _attachment@1 record." The handler never reads metadata records — it serves application/octet-stream and a bare Content-Disposition: attachment unless the caller passes ?contentType/?filename (attachments.ts:44-58). So of core #66's three sources, we implement two; the third (and the per-requester filename perspective the review affirmed) is missing.
  2. Forcing is a deny-list, not a safe-list. BLOCKED_MIME_TYPES (attachments.ts:73-82) is exactly the informal deny-list core #66 calls incomplete. Decided direction prefers the safe-list: image/* (minus image/svg+xml), video/*, audio/*, application/pdf, text/plain, application/octet-stream; everything else → application/octet-stream.
  3. Forcing must run after whichever source produced the candidate type — the current code does apply sanitizeMimeType to the param path and omits inference of blocked types, which happens to be result-shaped, but once the stored-metadata source lands the single application point should be explicit and tested per source.

Already correct, keep and pin: X-Content-Type-Options: nosniff on every attachment response; RFC 5987 filename encoding.

Work items

  • No-param path: resolve first-recorded mimeType for the fileId + requester's-own filename (falls back to first record's), per core #65
  • Replace the deny-list with the named safe-list policy once the spec section lands; one forcing function applied to the resolved result
  • mimeType-conflict rejection on _attachment@1 create/update arrives via core validation (#65) — server tests: second upload metadata with conflicting type → 422, matching type → OK, served Content-Type stable regardless of requester
  • Conformance fixtures (Core sync: adopt the shared conformance fixtures; fix the PATCH body shape; add the intent-split endpoints (core #52) #32): dangerous × each of three sources → forced; safe × each source → passes; nosniff present

Refs haverstack/core#65, haverstack/core#66, haverstack/core#52, #32, #33.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions