Files
skills/docs/api-stack/api/api-plugin-file-upload/src/validators/mime.md
2026-05-29 08:20:56 +01:00

2.5 KiB

mime.ts

Purpose

Cross-checks the declared Content-Type against the plugin's configured allow/deny lists AND (optionally) against the magic-bytes signature of the upload's first 4 KiB. This is the spoof-detection layer: a client uploading an .exe with Content-Type: text/plain is rejected because the sniffed type disagrees with the declared one.

Features

  • Three rejection vectors: denylist, allow-list (when non-empty), and sniff-mismatch.
  • Canonical lowercase type/subtype form returned so every downstream record stores a normalised value.
  • Structured context.reason ("deny-list", "allow-list", "sniff-mismatch", "malformed") so handlers and dashboards can branch on rejection cause without parsing message text.
  • Treats malformed declared Content-Type as a 415 rather than a 400 because the user payload was the data we couldn't accept — not the route shape.
  • Sniff comparison only fires when magic bytes produce a confident match (it never blocks unknown formats).

Usage: Basic

import { validateMime } from "@suaveplan/api-plugin-file-upload/validators/mime";

const canonical = validateMime({
    declaredMimeType: "image/png",
    allowedMimeTypes: ["image/png", "image/jpeg"],
    deniedMimeTypes: [],
});

Usage: Advanced

import { validateMime, FileUploadError } from "@suaveplan/api-plugin-file-upload";

const sample = new Uint8Array(buf.slice(0, 4096));
try {
    validateMime({
        declaredMimeType: "text/plain",
        allowedMimeTypes: [],
        deniedMimeTypes: [],
        sniffSample: sample,
    });
} catch (err) {
    if (err instanceof FileUploadError) {
        // err.context.reason === "sniff-mismatch"
    }
}

API Reference

  • ValidateMimeOptions — { declaredMimeType, allowedMimeTypes, deniedMimeTypes, sniffSample? }.
  • validateMime(opts: ValidateMimeOptions): string — returns the canonical type; throws FileUploadError(UPLOAD.MIME_REJECTED) on any rejection vector.

Implementation Notes

  • @suaveplan/mime's detectFromMagicBytes returns undefined for unrecognised payloads; we treat that as "no signal" and skip the cross-check, so unknown binary formats still upload successfully if the declared type is otherwise allowed.
  • The "allow-list" semantics only engage when the array is non-empty so the default config (allowedMimeTypes: []) means "rely on the denylist alone".
  • Comparisons are lowercase string-equality, not glob/regex, so callers needing image/* semantics should pre-expand the list at config time.