1 /**
2 * Copyright © DiamondMVC 2019
3 * License: MIT (https://github.com/DiamondMVC/Diamond/blob/master/LICENSE)
4 * Author: Jacob Jensen (bausshf)
5 */
6 module diamond.security.validation.file;
7
8 /// Alias to a file validator delegate.
9 public alias FileValidator = bool delegate(ubyte[]);
10
11 /// Collection of custom file validators.
12 private static __gshared FileValidator[string] _validators;
13
14 /**
15 * Adds a custom file validator.
16 * Params:
17 * extension = The file extension to validate.
18 * handler = The handler that validates the file.
19 */
20 void addCustomFileValidator(string extension, FileValidator handler)
21 {
22 _validators[extension] = handler;
23 }
24
25 /**
26 * Checks whether specified file data matches an extension.
27 * Currently this supports ".jpg, .jpeg, .gif, .png, .pdf"
28 * Params:
29 * extension = The extension of the file.
30 * data = The data to validate.
31 * Returns:
32 * True if the data is valid for the extension given, false otherwise. Unhandled extensions returns true.
33 */
34 bool isValidFile(string extension, ubyte[] data)
35 {
36 import diamond.errors.checks;
37
38 enforce(data && data.length, "No data to validate.");
39
40 auto customValidator = _validators.get(extension, null);
41
42 if (customValidator)
43 {
44 return customValidator(data);
45 }
46
47 switch (extension)
48 {
49 case ".jpg":
50 case ".jpeg":
51 {
52 return data.length > 4 &&
53 data[0] == 0xff && data[1] == 0xd8 &&
54 data[$-2] == 0xff && data[$-1] == 0xd9;
55 }
56
57 case ".gif":
58 {
59 if (data.length < 6)
60 {
61 return false;
62 }
63
64 auto start = cast(string)data[0 .. 6];
65
66 return start == "GIF87a" || start == "GIF89a";
67 }
68
69 case ".png":
70 {
71 if (data.length < 8)
72 {
73 return false;
74 }
75
76 auto png = data[1 .. 4];
77
78 if (png != "PNG")
79 {
80 return false;
81 }
82
83 return data[0] == 0x89 &&
84 data[4] == 0x0d && data[5] == 0x0a &&
85 data[6] == 0x1a &&
86 data[7] == 0x0a;
87 }
88
89 case ".pdf":
90 {
91 return data.length > 5 &&
92 data[0] == 0x25 &&
93 data[1] == 0x50 && data[2] == 0x44 && data[3] == 0x46 &&
94 data[4] == 0x2d;
95 }
96
97
98 default: return true;
99 }
100 }