@@ -12,11 +12,38 @@ const MALICIOUS_FILE_SIGNATURES = new Set([
1212export const MAX_FILE_SIZES = {
1313 'image/jpeg' : 10 * 1024 * 1024 ,
1414 'image/png' : 10 * 1024 * 1024 ,
15+ 'image/heic' : 10 * 1024 * 1024 ,
16+ 'image/heif' : 10 * 1024 * 1024 ,
1517} as const ;
1618
1719// Allowed MIME types
1820export const ALLOWED_MIME_TYPES = new Set ( Object . keys ( MAX_FILE_SIZES ) ) ;
1921
22+ // Common JPEG signatures from different devices
23+ const JPEG_SIGNATURES = new Set ( [
24+ 'FFD8FF' , // Standard JPEG SOI marker
25+ 'FFD8FFE0' , // JPEG/JFIF
26+ 'FFD8FFE1' , // JPEG/Exif (common in mobile phones)
27+ 'FFD8FFE2' , // JPEG/SPIFF
28+ 'FFD8FFE3' , // JPEG/JPEG-LS
29+ 'FFD8FFE8' , // JPEG/SPIFF
30+ 'FFD8FFED' , // JPEG/IPTC
31+ 'FFD8FFEE' , // JPEG/JPEG-LS
32+ ] ) ;
33+
34+ // Common PNG signatures
35+ const PNG_SIGNATURES = new Set ( [
36+ '89504E47' , // Standard PNG
37+ '89504E470D0A1A0A' , // Full PNG header
38+ ] ) ;
39+
40+ // HEIC/HEIF signatures
41+ const HEIC_SIGNATURES = new Set ( [
42+ '00000020667479706865696300' , // HEIC
43+ '0000001C667479706D696631' , // HEIF
44+ '00000018667479706D696631' , // HEIF variation
45+ ] ) ;
46+
2047/**
2148 * Checks if a buffer starts with any of the malicious file signatures
2249 */
@@ -30,13 +57,17 @@ const hasExecutableSignature = (buffer: Buffer): boolean => {
3057 * Validates the actual content type of a file using its magic numbers
3158 */
3259const validateFileType = ( buffer : Buffer , mimeType : string ) : boolean => {
33- const signature = buffer . slice ( 0 , 4 ) . toString ( 'hex' ) . toUpperCase ( ) ;
60+ // Get first 12 bytes to check for various signatures
61+ const signature = buffer . slice ( 0 , 12 ) . toString ( 'hex' ) . toUpperCase ( ) ;
3462
3563 switch ( mimeType ) {
3664 case 'image/jpeg' :
37- return signature . startsWith ( 'FFD8FF' ) ; // JPEG SOI marker
65+ return Array . from ( JPEG_SIGNATURES ) . some ( sig => signature . startsWith ( sig ) ) ;
3866 case 'image/png' :
39- return signature === '89504E47' ; // PNG signature
67+ return Array . from ( PNG_SIGNATURES ) . some ( sig => signature . startsWith ( sig ) ) ;
68+ case 'image/heic' :
69+ case 'image/heif' :
70+ return Array . from ( HEIC_SIGNATURES ) . some ( sig => signature . startsWith ( sig ) ) ;
4071 default :
4172 return false ;
4273 }
@@ -72,7 +103,7 @@ const calculateEntropy = (buffer: Buffer): number => {
72103export const validateFileSecurely = ( buffer : Buffer , mimeType : string ) : void => {
73104 // 1. Check if file type is allowed
74105 if ( ! ALLOWED_MIME_TYPES . has ( mimeType ) ) {
75- throw new BadRequestException ( 'Only JPEG and PNG images are allowed' ) ;
106+ throw new BadRequestException ( 'Only JPEG, PNG, and HEIC/HEIF images are allowed' ) ;
76107 }
77108
78109 // 2. Check file size
@@ -117,21 +148,33 @@ const validateImageStructure = (buffer: Buffer): void => {
117148 throw new Error ( 'File too small to be a valid image' ) ;
118149 }
119150
151+ const signature = buffer . slice ( 0 , 12 ) . toString ( 'hex' ) . toUpperCase ( ) ;
152+
120153 // For JPEG
121- if ( buffer [ 0 ] === 0xff && buffer [ 1 ] === 0xd8 ) {
154+ if ( Array . from ( JPEG_SIGNATURES ) . some ( sig => signature . startsWith ( sig ) ) ) {
122155 // Check for JPEG end marker
123156 if ( ! ( buffer [ buffer . length - 2 ] === 0xff && buffer [ buffer . length - 1 ] === 0xd9 ) ) {
124157 throw new Error ( 'Invalid JPEG structure' ) ;
125158 }
126159 }
127160 // For PNG
128- else if ( buffer . slice ( 0 , 8 ) . toString ( 'hex' ) . toUpperCase ( ) === '89504E470D0A1A0A' ) {
161+ else if ( signature . startsWith ( '89504E47' ) ) {
129162 // Check for IEND chunk
130163 const iendBuffer = Buffer . from ( [ 0x49 , 0x45 , 0x4e , 0x44 , 0xae , 0x42 , 0x60 , 0x82 ] ) ;
131164 if ( ! buffer . slice ( - 8 ) . equals ( iendBuffer ) ) {
132165 throw new Error ( 'Invalid PNG structure' ) ;
133166 }
134167 }
168+ // For HEIC/HEIF
169+ else if ( Array . from ( HEIC_SIGNATURES ) . some ( sig => signature . startsWith ( sig ) ) ) {
170+ // HEIC/HEIF validation is more complex, we'll do basic size validation
171+ if ( buffer . length < 512 ) {
172+ // HEIC files are typically larger
173+ throw new Error ( 'Invalid HEIC/HEIF structure' ) ;
174+ }
175+ } else {
176+ throw new Error ( 'Unsupported image format' ) ;
177+ }
135178} ;
136179
137180/**
0 commit comments