Repository navigation
Expand file tree
/
Copy patherrors.ts
More file actions
85 lines (80 loc) · 2.97 KB
/
Copy patherrors.ts
File metadata and controls
85 lines (80 loc) · 2.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/**
* The portable discriminant for a validation failure.
*
* The same three string values in all five ports — Go's `ValidationKind`, and
* the `kind` field added here, in Python, Rust and .NET. Catching by TYPE is not
* portable (Rust collapses to an enum, Go to one struct with a field), so this
* is what code that has to work the same in more than one language branches on.
* Pinned by `spec/error-taxonomy.json`.
*/
export const FileValidationKind = {
Size: 'size',
Mime: 'mime',
ContentMismatch: 'content_mismatch',
} as const;
export type FileValidationKind = (typeof FileValidationKind)[keyof typeof FileValidationKind];
/**
* Base class for all file validation errors.
* Consumers can `catch (err) { if (err instanceof FileValidationError) { ... } }`
* to uniformly map validation failures to an HTTP 400 or similar boundary response.
*/
export class FileValidationError extends Error {
constructor(
message: string,
public readonly kind: FileValidationKind,
) {
super(message);
this.name = 'FileValidationError';
}
}
/**
* Thrown when a file exceeds the declared `maxSize` during validation.
*/
export class FileSizeError extends FileValidationError {
constructor(
public readonly actualSize: number | undefined,
public readonly maxSize: number,
) {
super(
actualSize === undefined
? `File size is unknown; maxSize is ${maxSize} bytes`
: `File size (${actualSize} bytes) exceeds maximum allowed (${maxSize} bytes)`,
FileValidationKind.Size,
);
this.name = 'FileSizeError';
}
}
/**
* Thrown when a file's mime type is not in the declared `allowedMimes` list.
*/
export class FileMimeError extends FileValidationError {
constructor(
public readonly actualMimeType: string | undefined,
public readonly allowedMimes: readonly string[],
) {
super(
actualMimeType
? `File mime type "${actualMimeType}" is not in the allowed list: ${allowedMimes.join(', ')}`
: `File mime type is unknown; allowed types are: ${allowedMimes.join(', ')}`,
FileValidationKind.Mime,
);
this.name = 'FileMimeError';
}
}
/**
* Thrown when the magic-byte-detected mime type does not match the mime type
* claimed by the source (e.g. a `.php` file uploaded with `Content-Type: image/png`).
* This is the primary defense against mime-spoofing attacks for user uploads.
*/
export class FileContentMismatchError extends FileValidationError {
constructor(
public readonly claimedMimeType: string | undefined,
public readonly detectedMimeType: string | undefined,
) {
super(
`File content does not match claimed mime type. Claimed: ${claimedMimeType ?? 'unknown'}, detected: ${detectedMimeType ?? 'unknown'}`,
FileValidationKind.ContentMismatch,
);
this.name = 'FileContentMismatchError';
}
}