Skip to content

Commit 74335c4

Browse files
authored
Merge pull request #10 from ByteGuard-HQ/dev
Release v1.1.0
2 parents deaba3d + c9edf5e commit 74335c4

26 files changed

+514
-130
lines changed

Directory.Version.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<Project>
22
<PropertyGroup>
3-
<VersionPrefix>1.0.1</VersionPrefix>
3+
<VersionPrefix>1.1.0</VersionPrefix>
44
</PropertyGroup>
55
</Project>

README.md

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,18 @@ It helps you enforce consistent file upload rules by checking:
77
- File size limits
88
- File signatures (magic numbers) to detect spoofed types
99
- Specification conformance for Office Open XML / Open Document Formats (`.docx`, `.xlsx`, `.pptx`, `.odt`)
10+
- Malware scan result using a varity of scanners (_requires the addition of a specific ByteGuard.FileValidator scanner package_)
1011

11-
> ⚠️ **Important:** This library should be part of a **defense-in-depth** strategy.
12-
It does not replace antivirus scanning, sandboxing, or other security controls.
12+
> ⚠️ **Important:** This package is one layer in a defense-in-depth strategy.
13+
It does **not** replace endpoint protection, sandboxing, input validation, or other security controls.
1314

1415
## Features
1516

1617
- ✅ Validate files by **extension**
1718
- ✅ Validate files by **size**
1819
- ✅ Validate files by **signature (_magic-numbers_)**
1920
- ✅ Validate files by **specification conformance** for archive-based formats (_Open XML and Open Document Formats_)
21+
-**Ensure no malware** through a variety of antimalware scanners
2022
- ✅ Validate using file path, `Stream`, or `byte[]`
2123
- ✅ Configure which file types to support
2224
- ✅ Configure whether to **throw exceptions** or simply return a boolean
@@ -25,37 +27,45 @@ It does not replace antivirus scanning, sandboxing, or other security controls.
2527
## Getting Started
2628

2729
### Installation
28-
This package is published and installed via NuGet.
30+
This package is published and installed via [NuGet](https://www.nuget.org/packages/ByteGuard.FileValidator).
2931

3032
Reference the package in your project:
3133
```bash
3234
dotnet add package ByteGuard.FileValidator
3335
```
3436

37+
### Antimalware scanners
38+
In order to use the antimalware scanning capabilities, ensure you have a ByteGuard.FileValidator antimalware package referenced as well. Youo can find the relevant scanner package on NuGet under the namespace `ByteGuard.FileValidator.Scanners`.
3539
## Usage
3640

3741
### Basic validation
3842

3943
```csharp
44+
// Without antimalware scanner
4045
var configuration = new FileValidatorConfiguration
4146
{
42-
SupportedFileTypes = [FileExtensions.Pdf, FileExtensions.Jpg, FileExtensions.Png],
43-
FileSizeLimit = ByteSize.MegaBytes(25),
44-
ThrowExceptionOnInvalidFile = false
47+
SupportedFileTypes = [FileExtensions.Pdf, FileExtensions.Jpg, FileExtensions.Png],
48+
FileSizeLimit = ByteSize.MegaBytes(25),
49+
ThrowExceptionOnInvalidFile = false
4550
};
4651

4752
var fileValidator = new FileValidator(configuration);
4853
var isValid = fileValidator.IsValidFile("example.pdf", fileStream);
54+
55+
// With antimalware
56+
var antimalwareScanner = AntimalwareScannerImplementation();
57+
var fileValidator = new FileValidator(configuration, antimalwareScanner);
58+
var isValid = fileValidator.IsValidFile("example.pdf", fileStream);
4959
```
5060

5161
### Using the fluent builder
5262

5363
```csharp
5464
var configuration = new FileValidatorConfigurationBuilder()
55-
.AllowFileTypes(FileExtensions.Pdf, FileExtensions.Jpg, FileExtensions.Png)
56-
.SetFileSizeLimit(ByteSize.MegaBytes(25))
57-
.SetThrowExceptionOnInvalidFile(false)
58-
.Build();
65+
.AllowFileTypes(FileExtensions.Pdf, FileExtensions.Jpg, FileExtensions.Png)
66+
.SetFileSizeLimit(ByteSize.MegaBytes(25))
67+
.SetThrowExceptionOnInvalidFile(false)
68+
.Build();
5969

6070
var fileValidator = new FileValidator(configuration);
6171
var isValid = fileValidator.IsValidFile("example.pdf", fileStream);
@@ -72,13 +82,15 @@ The `FileValidator` class provides methods to validate specific aspects of a fil
7282
> 2. File size validation
7383
> 3. Signature (magic-number) validation
7484
> 4. Optional Open XML / Open Document Format specification conformance validation (for supported types)
85+
> 5. Optional antimalware scanning with a compatible scanning package
7586
7687
```csharp
7788
bool isExtensionValid = fileValidator.IsValidFileType(fileName);
7889
bool isFileSizeValid = fileValidator.HasValidSize(fileStream);
7990
bool isSignatureValid = fileValidator.HasValidSignature(fileName, fileStream);
8091
bool isOpenXmlValid = fileValidator.IsValidOpenXmlDocument(fileName, fileStream);
8192
bool isOpenDocumentFormatValid = fileValidator.IsValidOpenDocumentFormat(fileName, fileStream);
93+
bool isMalwareClean = fileValidator.IsMalwareClean(fileName, fileStream);
8294
```
8395

8496
### Example
@@ -88,14 +100,16 @@ public async Task<IActionResult> Upload(IFormFile file)
88100
{
89101
using var stream = file.OpenReadStream();
90102

103+
var antimalwareScanner = AntimalwareScannerImplementation();
104+
91105
var configuration = new FileValidatorConfiguration
92106
{
93107
SupportedFileTypes = [FileExtensions.Pdf, FileExtensions.Docx],
94108
FileSizeLimit = ByteSize.MegaBytes(10),
95109
ThrowExceptionOnInvalidFile = false
96110
};
97111

98-
var validator = new FileValidator(configuration);
112+
var validator = new FileValidator(configuration, antimalwareScanner);
99113

100114
if (!validator.IsValidFile(file.FileName, stream))
101115
{
@@ -132,9 +146,10 @@ The following file extensions are supported by the `FileValidator`:
132146

133147
`IsValidFile` always validates:
134148

135-
- File extension (against `SupportedFileTypes`)
136-
- File size (against `FileSizeLimit`)
137-
- File signature (magic number)
149+
- File extension (_against `SupportedFileTypes`_)
150+
- File size (_against `FileSizeLimit`_)
151+
- File signature (_magic number_)
152+
- Malware scan result (_if an antimalware scanner has been configured_)
138153

139154
For some formats, additional checks are performed:
140155

@@ -143,11 +158,13 @@ For some formats, additional checks are performed:
143158
- File size
144159
- Signature
145160
- Specification conformance
161+
- Malware scan result
146162

147163
- **Other binary formats** (e.g. images, audio, video such as `.jpg`, `.png`, `.mp3`, `.mp4`):
148164
- Extension
149165
- File size
150166
- Signature
167+
- Malware scan result
151168

152169
## Configuration Options
153170

@@ -171,6 +188,7 @@ When `ThrowExceptionOnInvalidFile` is set to `true`, validation functions will t
171188
| `InvalidSignatureException` | Thrown when the file's signature does not match the expected signature for its type. |
172189
| `InvalidOpenXmlFormatException` | Thrown when the internal structure of an Open XML file is invalid (`.docx`, `.xlsx`, `.pptx`, etc.). |
173190
| `InvalidOpenDocumentFormatException` | Thrown when the specification conformance of an Open Document Format file is invalid (`.odt`, etc.). |
191+
| `MalwareDetectedException` | Thrown when the configured antimalware scanner detected malware in the file from a scan result. |
174192

175193
## When to use this package
176194

assets/icon.png

6.92 KB
Loading

src/ByteGuard.FileValidator/ByteGuard.FileValidator.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>netstandard2.0;net48;net8.0;net9.0;net10.0</TargetFrameworks>
4+
<TargetFrameworks>netstandard2.0;net8.0;net9.0;net10.0</TargetFrameworks>
55
<Authors>ByteGuard Contributors, detilium</Authors>
66
<Description>ByteGuard File Validator is a security-focused .NET library for validating user-supplied files, providing a configurable API to help you enforce safe and consistent file handling across your applications.</Description>
77
<PackageProjectUrl>https://github.com/ByteGuard-HQ/byteguard-file-validator-net</PackageProjectUrl>
88
<RepositoryUrl>https://github.com/ByteGuard-HQ/byteguard-file-validator-net</RepositoryUrl>
99
<RepositoryType>git</RepositoryType>
1010
<PackageTags>byteguard file-validator file-validation file-upload upload-validation security application-security appsec</PackageTags>
1111
<PackageReadmeFile>README.md</PackageReadmeFile>
12+
<PackageIcon>icon.png</PackageIcon>
1213
<Copyright>Copyright © ByteGuard Contributors</Copyright>
1314
<PackageLicenseExpression>MIT</PackageLicenseExpression>
1415
</PropertyGroup>
@@ -19,6 +20,7 @@
1920

2021
<ItemGroup>
2122
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
23+
<None Include="..\..\assets\icon.png" Pack="true" PackagePath="\" />
2224
</ItemGroup>
2325

2426
<ItemGroup>

src/ByteGuard.FileValidator/ByteSize.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System;
2-
using System.Globalization;
1+
using System.Globalization;
32

43
namespace ByteGuard.FileValidator
54
{

src/ByteGuard.FileValidator/Configuration/ConfigurationValidator.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
using System;
2-
using System.Linq;
3-
using ByteGuard.FileValidator.Exceptions;
1+
using ByteGuard.FileValidator.Exceptions;
42

53
namespace ByteGuard.FileValidator.Configuration
64
{
@@ -49,4 +47,4 @@ public static void ThrowIfInvalid(FileValidatorConfiguration configuration)
4947
}
5048
}
5149
}
52-
}
50+
}
Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System.Collections.Generic;
2-
using ByteGuard.FileValidator.Exceptions;
1+
using ByteGuard.FileValidator.Scanners;
32

43
namespace ByteGuard.FileValidator.Configuration
54
{
@@ -26,34 +25,9 @@ public class FileValidatorConfiguration
2625
/// </remarks>
2726
public long FileSizeLimit { get; set; } = -1;
2827

29-
/// <summary>
30-
/// Maximum file size limit in string representation (e.g. "25MB", "2 GB", etc.).
31-
/// </summary>
32-
/// <remarks>
33-
/// Defines the file size limit of files. See <see cref="ByteSize"/> for conversion help.
34-
/// Will be ignored if <see cref="FileSizeLimit"/> is defined.
35-
/// Spacing (<c>"25 MB"</c> vs. <c>"25MB"</c>) is irrelevant.
36-
/// <para>Supported string representation are:
37-
/// <ul>
38-
/// <li><c>B</c>: Bytes</li>
39-
/// <li><c>KB</c>: Kilobytes</li>
40-
/// <li><c>MB</c>: Megabytes</li>
41-
/// <li><c>GB</c>: Gigabytes</li>
42-
/// </ul>
43-
/// </para>
44-
/// </remarks>
45-
public string FriendlyFileSizeLimit { get; set; }
46-
4728
/// <summary>
4829
/// Whether to throw an exception if an unsupported/invalid file is encountered. Defaults to <c>true</c>.
4930
/// </summary>
50-
/// <remarks>
51-
/// Will throw the following exceptions:
52-
/// <ul>
53-
/// <li><see cref="UnsupportedFileException"/> if the given file type is not supported according to the <see cref="SupportedFileTypes"/>.</li>
54-
/// <li><see cref="InvalidSignatureException"/> if the file signature does not match the valid signatures for the given file type.</li>
55-
/// </ul>
56-
/// </remarks>
5731
public bool ThrowExceptionOnInvalidFile { get; set; } = true;
5832
}
59-
}
33+
}

src/ByteGuard.FileValidator/Configuration/FileValidatorConfigurationBuilder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Collections.Generic;
1+
using ByteGuard.FileValidator.Scanners;
22

33
namespace ByteGuard.FileValidator.Configuration
44
{
@@ -61,4 +61,4 @@ public FileValidatorConfiguration Build()
6161
return configuration;
6262
}
6363
}
64-
}
64+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
namespace ByteGuard.FileValidator.Exceptions
2+
{
3+
/// <summary>
4+
/// Exception type used specifically when a an antimalware scanner failed to scane the file.
5+
/// </summary>
6+
public class AntimalwareScannerException : Exception
7+
{
8+
/// <summary>
9+
/// Default expcetion message for this expection type, if no custom message is provided.
10+
/// </summary>
11+
private const string defaultMessage = "An error occurred while scanning the file with the antimalware scanner.";
12+
13+
/// <summary>
14+
/// Initializes a new instance of the <see cref="AntimalwareScannerException"/> class indicating that an error occured during the scan.
15+
/// </summary>
16+
public AntimalwareScannerException()
17+
: this(defaultMessage)
18+
{
19+
}
20+
21+
/// <summary>
22+
/// Initializes a new instance of the <see cref="AntimalwareScannerException"/> class indicating that an error occured during the scan.
23+
/// </summary>
24+
/// <param name="message">Custom exception message.</param>
25+
public AntimalwareScannerException(string message) : base(message)
26+
{
27+
}
28+
29+
/// <summary>
30+
/// Initializes a new instance of the <see cref="AntimalwareScannerException"/> class indicating that an error occured during the scan.
31+
/// </summary>
32+
/// <param name="message">Custom exception message.</param>
33+
/// <param name="innerException">Inner exception.</param>
34+
public AntimalwareScannerException(string message, Exception innerException) : base(message, innerException)
35+
{
36+
}
37+
38+
/// <summary>
39+
/// Initializes a new instance of the <see cref="AntimalwareScannerException"/> class indicating that an error occured during the scan.
40+
/// </summary>
41+
/// <param name="innerException">Inner exception.</param>
42+
public AntimalwareScannerException(Exception innerException) : base(defaultMessage, innerException)
43+
{
44+
}
45+
}
46+
}
Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,33 @@
1-
using System;
2-
3-
namespace ByteGuard.FileValidator.Exceptions
1+
namespace ByteGuard.FileValidator.Exceptions
42
{
53
/// <summary>
64
/// Exception type used specifically when a given file does not contain any content.
75
/// </summary>
86
public class EmptyFileException : Exception
97
{
8+
/// <summary>
9+
/// Construct a new <see cref="EmptyFileException"/> to indicate that the provided file does not have any content.
10+
/// </summary>
1011
public EmptyFileException()
1112
: this("The provided file does not have any content.")
1213
{
1314
}
1415

16+
/// <summary>
17+
/// Construct a new <see cref="EmptyFileException"/> to indicate that the provided file does not have any content.
18+
/// </summary>
19+
/// <param name="message">Custom exception message.</param>
1520
public EmptyFileException(string message) : base(message)
1621
{
1722
}
1823

24+
/// <summary>
25+
/// Construct a new <see cref="EmptyFileException"/> to indicate that the provided file does not have any content.
26+
/// </summary>
27+
/// <param name="message">Custom exception message.</param>
28+
/// <param name="innerException">Inner exception.</param>
1929
public EmptyFileException(string message, Exception innerException) : base(message, innerException)
2030
{
2131
}
2232
}
23-
}
33+
}

0 commit comments

Comments
 (0)