diff --git a/src/ByteGuard.FileValidator/Configuration/ConfigurationValidator.cs b/src/ByteGuard.FileValidator/Configuration/ConfigurationValidator.cs
index 88cfcba..bfca612 100644
--- a/src/ByteGuard.FileValidator/Configuration/ConfigurationValidator.cs
+++ b/src/ByteGuard.FileValidator/Configuration/ConfigurationValidator.cs
@@ -36,7 +36,7 @@ public static void ThrowIfInvalid(FileValidatorConfiguration configuration)
}
// Validate file type is supported by the current version of FileValidator.
- if (!FileValidator.SupportedFileDefinitions.Any(f => f.FileType.Equals(fileType)))
+ if (!FileDefinitions.SupportedFileDefinitions.Any(f => f.FileType.Equals(fileType)))
{
throw new UnsupportedFileException($"File type '{fileType}' is not supported in the current version of FileValidator.");
}
diff --git a/src/ByteGuard.FileValidator/FileDefinitions.cs b/src/ByteGuard.FileValidator/FileDefinitions.cs
new file mode 100644
index 0000000..34c6f16
--- /dev/null
+++ b/src/ByteGuard.FileValidator/FileDefinitions.cs
@@ -0,0 +1,316 @@
+using ByteGuard.FileValidator.Models;
+
+namespace ByteGuard.FileValidator;
+
+internal static class FileDefinitions
+{
+ ///
+ /// List of all supported valid file definitions, incl. their header signatures (magic numbers)
+ /// and potentially their corresponding valid subtype signatures.
+ ///
+ internal static readonly List SupportedFileDefinitions = new List
+ {
+ new FileDefinition
+ {
+ FileType = FileExtensions.Jpeg,
+ ValidSignatures = new List
+ {
+ new byte[] { 0xFF, 0xD8, 0xFF } // ÿØÿ
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Jpg,
+ ValidSignatures = new List
+ {
+ new byte[] { 0xFF, 0xD8, 0xFF } // ÿØÿ
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Jpe,
+ ValidSignatures = new List
+ {
+ new byte[] { 0xFF, 0xD8, 0xFF } // ÿØÿ
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Pdf,
+ ValidSignatures = new List
+ {
+ new byte[] { 0x25, 0x50, 0x44, 0x46, 0x2D } // %PDF-
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Png,
+ ValidSignatures = new List
+ {
+ new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A } // ‰PNG␍␊␚␊
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Bmp,
+ ValidSignatures = new List
+ {
+ new byte[] { 0x42, 0x4D } // BM
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Doc,
+ ValidSignatures = new List
+ {
+ new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 } // ÐÏ␑ࡱ␚á
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Docx,
+ // WARNING: This shares the same signature as .zip and could potentially allow for .zip disguised as .docx.
+ ValidSignatures = new List
+ {
+ new byte[] { 0x50, 0x4B, 0x03, 0x04 } // PK␃␄
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Odp,
+ // WARNING: This shares the same signature as .zip and could potentially allow for .zip disguised as .odp.
+ ValidSignatures = new List
+ {
+ new byte[] { 0x50, 0x4B, 0x03, 0x04 } // PK␃␄
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Ods,
+ // WARNING: This shares the same signature as .zip and could potentially allow for .zip disguised as .ods.
+ ValidSignatures = new List
+ {
+ new byte[] { 0x50, 0x4B, 0x03, 0x04 } // PK␃␄
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Odt,
+ // WARNING: This shares the same signature as .zip and could potentially allow for .zip disguised as .odt.
+ ValidSignatures = new List
+ {
+ new byte[] { 0x50, 0x4B, 0x03, 0x04 } // PK␃␄
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Rtf,
+ ValidSignatures = new List
+ {
+ new byte[] { 0x7B, 0x5C, 0x72, 0x74, 0x66, 0x31 } // {\rtf1
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Xlsx,
+ // WARNING: This shares the same signature as .zip and could potentially allow for .zip disguised as .xlsx.
+ ValidSignatures = new List
+ {
+ new byte[] { 0x50, 0x4B, 0x03, 0x04 } // PK␃␄
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Xls,
+ ValidSignatures = new List
+ {
+ new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 } // ÐÏ␑ࡱ␚á
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Pptx,
+ // WARNING: This shares the same signature as .zip and could potentially allow for .zip disguised as .pptx.
+ ValidSignatures = new List
+ {
+ new byte[] { 0x50, 0x4B, 0x03, 0x04 } // PK␃␄
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.M4a,
+ ValidSignatures = new List
+ {
+ new byte[] { 0x66, 0x74, 0x79, 0x70 } // ftyp
+ },
+ SignatureOffset = 4,
+ HasSubtype = true,
+ SubtypeOffset = 8,
+ ValidSubtypeSignatures = new List
+ {
+ new byte[] { 0x4D, 0x34, 0x41, 0x20 } // M4A_
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Mov,
+ ValidSignatures = new List
+ {
+ new byte[] { 0x66, 0x74, 0x79, 0x70 } // ftyp
+ },
+ SignatureOffset = 4,
+ HasSubtype = true,
+ SubtypeOffset = 8,
+ ValidSubtypeSignatures = new List
+ {
+ new byte[] { 0x71, 0x74, 0x20, 0x20 } // qt__ (Quicktime)
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Avi,
+ ValidSignatures = new List
+ {
+ new byte[] { 0x52, 0x49, 0x46, 0x46 } // RIFF
+ },
+ HasSubtype = true,
+ SubtypeOffset = 8,
+ ValidSubtypeSignatures = new List
+ {
+ new byte[] { 0x41, 0x56, 0x49, 0x20 } // AVI_
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Mp3,
+ ValidSignatures = new List
+ {
+ new byte[] { 0xFF, 0xFB }, // Without ID3 tag
+ new byte[] { 0xFF, 0xF2 }, // Without ID3 tag
+ new byte[] { 0xFF, 0xF3 }, // Without ID3 tag
+ new byte[] { 0x49, 0x44, 0x33 } // ID3
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Mp4,
+ ValidSignatures = new List
+ {
+ new byte[] { 0x66, 0x74, 0x79, 0x70 } // ftyp
+ },
+ SignatureOffset = 4,
+ HasSubtype = true,
+ SubtypeOffset = 8,
+ ValidSubtypeSignatures = new List
+ {
+ new byte[] { 0x6D, 0x6D, 0x70, 0x34 }, // mmp4 (MP4)
+ new byte[] { 0x6D, 0x70, 0x34, 0x32 }, // mp42 (MP4 v2)
+ new byte[] { 0x69, 0x73, 0x6F, 0x6D }, // isom (ISO Base Media File)
+ new byte[] { 0x4D, 0x53, 0x4E, 0x56 } // MSNV (MPEG-4)
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Wav,
+ ValidSignatures = new List
+ {
+ new byte[] { 0x52, 0x49, 0x46, 0x46 } // RIFF
+ },
+ HasSubtype = true,
+ SubtypeOffset = 8,
+ ValidSubtypeSignatures = new List
+ {
+ new byte[] { 0x57, 0x41, 0x56, 0x45 } // WAVE
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Txt,
+ AllowMissingSignature = true
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Csv,
+ AllowMissingSignature = true
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Ico,
+ ValidSignatures = new List
+ {
+ new byte[] { 0x00, 0x00, 0x01, 0x00 }, // ␀␀␁␀
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Heic,
+ ValidSignatures = new List
+ {
+ new byte[] { 0x66, 0x74, 0x79, 0x70, 0x68, 0x65, 0x69, 0x63 } // ftypheic
+ },
+ SignatureOffset = 4
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Gif,
+ ValidSignatures = new List
+ {
+ new byte[] { 0x47, 0x49, 0x46, 0x38 }, // GIF8
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Tif,
+ ValidSignatures = new List
+ {
+ new byte[] { 0x49, 0x49, 0x2A, 0x00 }, // II*␀ (Intel byte order)
+ new byte[] { 0x4D, 0x4D, 0x00, 0x2A }, // MM␀* (Motorola byte order)
+ new byte[] { 0x49, 0x49, 0x2B, 0x00 }, // II+␀ (BigTIFF, Intel byte order)
+ new byte[] { 0x4D, 0x4D, 0x00, 0x2B } // MM␀+ (BigTIFF, Motorola byte order)
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Tiff,
+ ValidSignatures = new List
+ {
+ new byte[] { 0x49, 0x49, 0x2A, 0x00 }, // II*␀ (Intel byte order)
+ new byte[] { 0x4D, 0x4D, 0x00, 0x2A }, // MM␀* (Motorola byte order)
+ new byte[] { 0x49, 0x49, 0x2B, 0x00 }, // II+␀ (BigTIFF, Intel byte order)
+ new byte[] { 0x4D, 0x4D, 0x00, 0x2B } // MM␀+ (BigTIFF, Motorola byte order)
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Ogg,
+ ValidSignatures = new List
+ {
+ new byte[] { 0x4F, 0x67, 0x67, 0x53 } // OggS
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Oga,
+ ValidSignatures = new List
+ {
+ new byte[] { 0x4F, 0x67, 0x67, 0x53 } // OggS
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Ogv,
+ ValidSignatures = new List
+ {
+ new byte[] { 0x4F, 0x67, 0x67, 0x53 } // OggS
+ }
+ },
+ new FileDefinition
+ {
+ FileType = FileExtensions.Ogx,
+ ValidSignatures = new List
+ {
+ new byte[] { 0x4F, 0x67, 0x67, 0x53 } // OggS
+ }
+ }
+ };
+}
diff --git a/src/ByteGuard.FileValidator/FileValidator.cs b/src/ByteGuard.FileValidator/FileValidator.cs
index 11040ab..77333c0 100644
--- a/src/ByteGuard.FileValidator/FileValidator.cs
+++ b/src/ByteGuard.FileValidator/FileValidator.cs
@@ -1,7 +1,6 @@
using DocumentFormat.OpenXml.Packaging;
using ByteGuard.FileValidator.Configuration;
using ByteGuard.FileValidator.Exceptions;
-using ByteGuard.FileValidator.Models;
using ByteGuard.FileValidator.Validators;
using ByteGuard.FileValidator.Scanners;
@@ -12,316 +11,6 @@ namespace ByteGuard.FileValidator
///
public class FileValidator
{
- ///
- /// List of all supported valid file definitions, incl. their header signatures (magic numbers)
- /// and potentially their corresponding valid subtype signatures.
- ///
- internal static readonly List SupportedFileDefinitions = new List
- {
- new FileDefinition
- {
- FileType = FileExtensions.Jpeg,
- ValidSignatures = new List
- {
- new byte[] { 0xFF, 0xD8, 0xFF } // ÿØÿ
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Jpg,
- ValidSignatures = new List
- {
- new byte[] { 0xFF, 0xD8, 0xFF } // ÿØÿ
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Jpe,
- ValidSignatures = new List
- {
- new byte[] { 0xFF, 0xD8, 0xFF } // ÿØÿ
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Pdf,
- ValidSignatures = new List
- {
- new byte[] { 0x25, 0x50, 0x44, 0x46, 0x2D } // %PDF-
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Png,
- ValidSignatures = new List
- {
- new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A } // ‰PNG␍␊␚␊
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Bmp,
- ValidSignatures = new List
- {
- new byte[] { 0x42, 0x4D } // BM
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Doc,
- ValidSignatures = new List
- {
- new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 } // ÐÏ␑ࡱ␚á
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Docx,
- // WARNING: This shares the same signature as .zip and could potentially allow for .zip disguised as .docx.
- ValidSignatures = new List
- {
- new byte[] { 0x50, 0x4B, 0x03, 0x04 } // PK␃␄
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Odp,
- // WARNING: This shares the same signature as .zip and could potentially allow for .zip disguised as .odp.
- ValidSignatures = new List
- {
- new byte[] { 0x50, 0x4B, 0x03, 0x04 } // PK␃␄
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Ods,
- // WARNING: This shares the same signature as .zip and could potentially allow for .zip disguised as .ods.
- ValidSignatures = new List
- {
- new byte[] { 0x50, 0x4B, 0x03, 0x04 } // PK␃␄
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Odt,
- // WARNING: This shares the same signature as .zip and could potentially allow for .zip disguised as .odt.
- ValidSignatures = new List
- {
- new byte[] { 0x50, 0x4B, 0x03, 0x04 } // PK␃␄
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Rtf,
- ValidSignatures = new List
- {
- new byte[] { 0x7B, 0x5C, 0x72, 0x74, 0x66, 0x31 } // {\rtf1
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Xlsx,
- // WARNING: This shares the same signature as .zip and could potentially allow for .zip disguised as .xlsx.
- ValidSignatures = new List
- {
- new byte[] { 0x50, 0x4B, 0x03, 0x04 } // PK␃␄
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Xls,
- ValidSignatures = new List
- {
- new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 } // ÐÏ␑ࡱ␚á
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Pptx,
- // WARNING: This shares the same signature as .zip and could potentially allow for .zip disguised as .pptx.
- ValidSignatures = new List
- {
- new byte[] { 0x50, 0x4B, 0x03, 0x04 } // PK␃␄
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.M4a,
- ValidSignatures = new List
- {
- new byte[] { 0x66, 0x74, 0x79, 0x70 } // ftyp
- },
- SignatureOffset = 4,
- HasSubtype = true,
- SubtypeOffset = 8,
- ValidSubtypeSignatures = new List
- {
- new byte[] { 0x4D, 0x34, 0x41, 0x20 } // M4A_
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Mov,
- ValidSignatures = new List
- {
- new byte[] { 0x66, 0x74, 0x79, 0x70 } // ftyp
- },
- SignatureOffset = 4,
- HasSubtype = true,
- SubtypeOffset = 8,
- ValidSubtypeSignatures = new List
- {
- new byte[] { 0x71, 0x74, 0x20, 0x20 } // qt__ (Quicktime)
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Avi,
- ValidSignatures = new List
- {
- new byte[] { 0x52, 0x49, 0x46, 0x46 } // RIFF
- },
- HasSubtype = true,
- SubtypeOffset = 8,
- ValidSubtypeSignatures = new List
- {
- new byte[] { 0x41, 0x56, 0x49, 0x20 } // AVI_
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Mp3,
- ValidSignatures = new List
- {
- new byte[] { 0xFF, 0xFB }, // Without ID3 tag
- new byte[] { 0xFF, 0xF2 }, // Without ID3 tag
- new byte[] { 0xFF, 0xF3 }, // Without ID3 tag
- new byte[] { 0x49, 0x44, 0x33 } // ID3
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Mp4,
- ValidSignatures = new List
- {
- new byte[] { 0x66, 0x74, 0x79, 0x70 } // ftyp
- },
- SignatureOffset = 4,
- HasSubtype = true,
- SubtypeOffset = 8,
- ValidSubtypeSignatures = new List
- {
- new byte[] { 0x6D, 0x6D, 0x70, 0x34 }, // mmp4 (MP4)
- new byte[] { 0x6D, 0x70, 0x34, 0x32 }, // mp42 (MP4 v2)
- new byte[] { 0x69, 0x73, 0x6F, 0x6D }, // isom (ISO Base Media File)
- new byte[] { 0x4D, 0x53, 0x4E, 0x56 } // MSNV (MPEG-4)
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Wav,
- ValidSignatures = new List
- {
- new byte[] { 0x52, 0x49, 0x46, 0x46 } // RIFF
- },
- HasSubtype = true,
- SubtypeOffset = 8,
- ValidSubtypeSignatures = new List
- {
- new byte[] { 0x57, 0x41, 0x56, 0x45 } // WAVE
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Txt,
- AllowMissingSignature = true
- },
- new FileDefinition
- {
- FileType = FileExtensions.Csv,
- AllowMissingSignature = true
- },
- new FileDefinition
- {
- FileType = FileExtensions.Ico,
- ValidSignatures = new List
- {
- new byte[] { 0x00, 0x00, 0x01, 0x00 }, // ␀␀␁␀
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Heic,
- ValidSignatures = new List
- {
- new byte[] { 0x66, 0x74, 0x79, 0x70, 0x68, 0x65, 0x69, 0x63 } // ftypheic
- },
- SignatureOffset = 4
- },
- new FileDefinition
- {
- FileType = FileExtensions.Gif,
- ValidSignatures = new List
- {
- new byte[] { 0x47, 0x49, 0x46, 0x38 }, // GIF8
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Tif,
- ValidSignatures = new List
- {
- new byte[] { 0x49, 0x49, 0x2A, 0x00 }, // II*␀ (Intel byte order)
- new byte[] { 0x4D, 0x4D, 0x00, 0x2A }, // MM␀* (Motorola byte order)
- new byte[] { 0x49, 0x49, 0x2B, 0x00 }, // II+␀ (BigTIFF, Intel byte order)
- new byte[] { 0x4D, 0x4D, 0x00, 0x2B } // MM␀+ (BigTIFF, Motorola byte order)
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Tiff,
- ValidSignatures = new List
- {
- new byte[] { 0x49, 0x49, 0x2A, 0x00 }, // II*␀ (Intel byte order)
- new byte[] { 0x4D, 0x4D, 0x00, 0x2A }, // MM␀* (Motorola byte order)
- new byte[] { 0x49, 0x49, 0x2B, 0x00 }, // II+␀ (BigTIFF, Intel byte order)
- new byte[] { 0x4D, 0x4D, 0x00, 0x2B } // MM␀+ (BigTIFF, Motorola byte order)
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Ogg,
- ValidSignatures = new List
- {
- new byte[] { 0x4F, 0x67, 0x67, 0x53 } // OggS
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Oga,
- ValidSignatures = new List
- {
- new byte[] { 0x4F, 0x67, 0x67, 0x53 } // OggS
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Ogv,
- ValidSignatures = new List
- {
- new byte[] { 0x4F, 0x67, 0x67, 0x53 } // OggS
- }
- },
- new FileDefinition
- {
- FileType = FileExtensions.Ogx,
- ValidSignatures = new List
- {
- new byte[] { 0x4F, 0x67, 0x67, 0x53 } // OggS
- }
- }
- };
-
///
/// Specific file extensions for files that are Open XML documents.
/// These require extra care when validating, as Open XML files are ZIP-archives and can contain potentially harmful content.
@@ -543,7 +232,7 @@ public bool IsValidFileType(string fileName)
{
var extension = Path.GetExtension(fileName).ToLowerInvariant();
var isSupported = _configuration.SupportedFileTypes.Contains(extension, StringComparer.InvariantCultureIgnoreCase) &&
- SupportedFileDefinitions.Any(fd => fd.FileType.Equals(extension, StringComparison.InvariantCultureIgnoreCase));
+ FileDefinitions.SupportedFileDefinitions.Any(fd => fd.FileType.Equals(extension, StringComparison.InvariantCultureIgnoreCase));
if (!isSupported && _configuration.ThrowExceptionOnInvalidFile)
{
@@ -598,7 +287,7 @@ public bool HasValidSignature(string fileName, Stream stream)
throw new InvalidOperationException("Stream is not seekable.");
}
- var fileDefinition = SupportedFileDefinitions.FirstOrDefault(fd =>
+ var fileDefinition = FileDefinitions.SupportedFileDefinitions.FirstOrDefault(fd =>
fd.FileType.Equals(extension, StringComparison.InvariantCultureIgnoreCase));
if (fileDefinition == null)