mirror of
https://github.com/xoofx/markdig.git
synced 2026-02-04 05:44:50 +00:00
Add a basic fuzzing project (#903)
* Add basic fuzzing project * Mark the project as non-packable
This commit is contained in:
4
src/Markdig.Fuzzing/.gitignore
vendored
Normal file
4
src/Markdig.Fuzzing/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
corpus
|
||||
libfuzzer-dotnet-windows.exe
|
||||
crash-*
|
||||
timeout-*
|
||||
19
src/Markdig.Fuzzing/Markdig.Fuzzing.csproj
Normal file
19
src/Markdig.Fuzzing/Markdig.Fuzzing.csproj
Normal file
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SharpFuzz" Version="2.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Markdig\Markdig.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
71
src/Markdig.Fuzzing/Program.cs
Normal file
71
src/Markdig.Fuzzing/Program.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using Markdig;
|
||||
using Markdig.Renderers.Roundtrip;
|
||||
using Markdig.Syntax;
|
||||
using SharpFuzz;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
ReadOnlySpanAction fuzzTarget = ParseRenderFuzzer.FuzzTarget;
|
||||
|
||||
if (args.Length > 0)
|
||||
{
|
||||
// Run the target on existing inputs
|
||||
string[] files = Directory.Exists(args[0])
|
||||
? Directory.GetFiles(args[0])
|
||||
: [args[0]];
|
||||
|
||||
Debugger.Launch();
|
||||
|
||||
foreach (string inputFile in files)
|
||||
{
|
||||
fuzzTarget(File.ReadAllBytes(inputFile));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Fuzzer.LibFuzzer.Run(fuzzTarget);
|
||||
}
|
||||
|
||||
sealed class ParseRenderFuzzer
|
||||
{
|
||||
private static readonly MarkdownPipeline s_advancedPipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
private static readonly ResettableRoundtripRenderer _roundtripRenderer = new();
|
||||
|
||||
public static void FuzzTarget(ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
string text = Encoding.UTF8.GetString(bytes);
|
||||
|
||||
try
|
||||
{
|
||||
MarkdownDocument document = Markdown.Parse(text);
|
||||
_ = document.ToHtml();
|
||||
|
||||
document = Markdown.Parse(text, s_advancedPipeline);
|
||||
_ = document.ToHtml(s_advancedPipeline);
|
||||
|
||||
document = Markdown.Parse(text, trackTrivia: true);
|
||||
_ = document.ToHtml();
|
||||
_roundtripRenderer.Reset();
|
||||
_roundtripRenderer.Render(document);
|
||||
|
||||
_ = Markdown.Normalize(text);
|
||||
_ = Markdown.ToPlainText(text);
|
||||
}
|
||||
catch (Exception ex) when (IsIgnorableException(ex)) { }
|
||||
}
|
||||
|
||||
private static bool IsIgnorableException(Exception exception)
|
||||
{
|
||||
return exception.Message.Contains("Markdown elements in the input are too deeply nested", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private sealed class ResettableRoundtripRenderer : RoundtripRenderer
|
||||
{
|
||||
public ResettableRoundtripRenderer() : base(new StringWriter(new StringBuilder(1024 * 1024))) { }
|
||||
|
||||
public new void Reset() => base.Reset();
|
||||
}
|
||||
}
|
||||
86
src/Markdig.Fuzzing/run-fuzzer.ps1
Normal file
86
src/Markdig.Fuzzing/run-fuzzer.ps1
Normal file
@@ -0,0 +1,86 @@
|
||||
param (
|
||||
[string]$configuration = $null
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
|
||||
$libFuzzer = "libfuzzer-dotnet-windows.exe"
|
||||
$outputDir = "bin"
|
||||
|
||||
function Get-LibFuzzer {
|
||||
param (
|
||||
[string]$Path
|
||||
)
|
||||
|
||||
$libFuzzerUrl = "https://github.com/Metalnem/libfuzzer-dotnet/releases/download/v2025.05.02.0904/libfuzzer-dotnet-windows.exe"
|
||||
$expectedHash = "17af5b3f6ff4d2c57b44b9a35c13051b570eb66f0557d00015df3832709050bf"
|
||||
|
||||
Write-Output "Downloading libFuzzer from $libFuzzerUrl..."
|
||||
|
||||
try {
|
||||
$tempFile = "$Path.tmp"
|
||||
Invoke-WebRequest -Uri $libFuzzerUrl -OutFile $tempFile -UseBasicParsing
|
||||
|
||||
$downloadedHash = (Get-FileHash -Path $tempFile -Algorithm SHA256).Hash
|
||||
|
||||
if ($downloadedHash -eq $ExpectedHash) {
|
||||
Move-Item -Path $tempFile -Destination $Path -Force
|
||||
Write-Output "libFuzzer downloaded successfully to $Path"
|
||||
}
|
||||
else {
|
||||
Write-Error "Hash validation failed."
|
||||
Remove-Item -Path $tempFile -Force -ErrorAction SilentlyContinue
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Error "Failed to download libFuzzer: $($_.Exception.Message)"
|
||||
Remove-Item -Path $tempFile -Force -ErrorAction SilentlyContinue
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Check if libFuzzer exists, download if not
|
||||
if (-not (Test-Path $libFuzzer)) {
|
||||
Get-LibFuzzer -Path $libFuzzer
|
||||
}
|
||||
|
||||
$toolListOutput = dotnet tool list --global sharpFuzz.CommandLine 2>$null
|
||||
if (-not ($toolListOutput -match "sharpfuzz")) {
|
||||
Write-Output "Installing sharpfuzz CLI"
|
||||
dotnet tool install --global sharpFuzz.CommandLine
|
||||
}
|
||||
|
||||
if (Test-Path $outputDir) {
|
||||
Remove-Item -Recurse -Force $outputDir
|
||||
}
|
||||
|
||||
if ($configuration -eq $null) {
|
||||
$configuration = "Debug"
|
||||
}
|
||||
|
||||
dotnet publish -c $configuration -o $outputDir
|
||||
|
||||
$project = Join-Path $outputDir "Markdig.Fuzzing.dll"
|
||||
|
||||
$fuzzingTarget = Join-Path $outputDir "Markdig.dll"
|
||||
|
||||
Write-Output "Instrumenting $fuzzingTarget"
|
||||
& sharpfuzz $fuzzingTarget
|
||||
|
||||
if ($LastExitCode -ne 0) {
|
||||
Write-Error "An error occurred while instrumenting $fuzzingTarget"
|
||||
exit 1
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Force -Path corpus | Out-Null
|
||||
|
||||
$libFuzzerArgs = @("--target_path=dotnet", "--target_arg=$project", "-timeout=10", "corpus")
|
||||
|
||||
# Add any additional arguments passed to the script
|
||||
if ($args) {
|
||||
$libFuzzerArgs += $args
|
||||
}
|
||||
|
||||
Write-Output "Starting libFuzzer with arguments: $libFuzzerArgs"
|
||||
& ./$libFuzzer @libFuzzerArgs
|
||||
@@ -12,6 +12,9 @@
|
||||
<Project Path="Markdig.Benchmarks/Markdig.Benchmarks.csproj">
|
||||
<BuildDependency Project="Markdig/Markdig.csproj" />
|
||||
</Project>
|
||||
<Project Path="Markdig.Fuzzing/Markdig.Fuzzing.csproj">
|
||||
<BuildDependency Project="Markdig/Markdig.csproj" />
|
||||
</Project>
|
||||
<Project Path="Markdig.Tests/Markdig.Tests.csproj">
|
||||
<BuildDependency Project="Markdig/Markdig.csproj" />
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user