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 }