first commit
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
# `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
|
||||
|
||||
```ts
|
||||
import { validateMime } from "@suaveplan/api-plugin-file-upload/validators/mime";
|
||||
|
||||
const canonical = validateMime({
|
||||
declaredMimeType: "image/png",
|
||||
allowedMimeTypes: ["image/png", "image/jpeg"],
|
||||
deniedMimeTypes: [],
|
||||
});
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
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.
|
||||
Reference in New Issue
Block a user