Official package SourceLink checksums require CR/LF normalization
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
### Description
Several official .NET package PDBs appear to contain source document checksums computed over CRLF-normalized source files, while SourceLink points at GitHub raw URLs that serve LF-normalized blobs.
This means strict SourceLink/PDB byte verification fails, but the same files verify after CR/LF normalization. This does **not** look like source-content drift; it looks like line-ending drift between official build inputs and the Git blob bytes addressed by SourceLink.
This was found with `dotnet-inspect` 0.9.0, which reports both strict content mismatches and CR/LF-normalized matches.
### Repro
Install/use dotnet-inspect 0.9.0 or newer:
```bash
dotnet tool install -g dotnet-inspect --version 0.9.0
```
Run SourceLink Integrity on a runtime-owned package:
```bash
dotnet-inspect library System.Text.Json.dll --package System.Text.Json -S "SourceLink Integrity"
```
Observed output shape:
```md
## SourceLink Integrity
| Field | Value |
| ----- | ----- |
| Mismatched | 0 |
| CR/LF Mismatch | 338 normalized |
| Status | Partial |
| Unverifiable | 2 |
| Verified | 338 |
```
A package with no unverifiable source documents shows the same line-ending pattern more cleanly:
```bash
dotnet-inspect library System.IO.Pipelines.dll --package System.IO.Pipelines -S "SourceLink Integrity"
```
```md
| Mismatched | 0 |
| CR/LF Mismatch | 30 normalized |
| Status | Verified |
| Unverifiable | 0 |
| Verified | 30 |
```
### Expected behavior
For SourceLink URLs pinned to immutable Git commits, the source bytes fetched from SourceLink should match the checksums recorded in the portable PDB without line-ending normalization.
### Actual behavior
For every sampled source document in the affected packages, strict hash comparison fails, but comparison succeeds after normalizing LF/CRLF. For example, for `System.Text.Json`, all 338 verified files required CR/LF normalization.
### Package sample
Representative packages tested with `dotnet-inspect 0.9.0`:
| Package | Source repo | Status | Verified | CR/LF normalized | Mismatched | Unverifiable |
| --- | --- | --- | ---: | ---: | ---: | ---: |
| [System.Text.Json](https://www.nuget.org/packages/System.Text.Json) | runtime | Partial | 338 | 338 | 0 | 2 |
| [System.Text.Encodings.Web](https://www.nuget.org/packages/System.Text.Encodings.Web) | runtime | Partial | 27 | 27 | 0 | 2 |
| [System.IO.Pipelines](https://www.nuget.org/packages/System.IO.Pipelines) | runtime | Verified | 30 | 30 | 0 | 0 |
| [System.Collections.Immutable](https://www.nuget.org/packages/System.Collections.Immutable) | runtime | Verified | 130 | 130 | 0 | 0 |
| [System.Memory](https://www.nuget.org/packages/System.Memory) | runtime | Verified | 101 | 101 | 0 | 0 |
| [System.Buffers](https://www.nuget.org/packages/System.Buffers) | runtime | Verified | 6 | 6 | 0 | 0 |
| [System.Diagnostics.DiagnosticSource](https://www.nuget.org/packages/System.Diagnostics.DiagnosticSource) | runtime | Partial | 76 | 76 | 0 | 2 |
| [System.Formats.Asn1](https://www.nuget.org/packages/System.Formats.Asn1) | runtime | Partial | 42 | 42 | 0 | 2 |
| [Microsoft.Bcl.AsyncInterfaces](https://www.nuget.org/packages/Microsoft.Bcl.AsyncInterfaces) | runtime | Verified | 4 | 4 | 0 | 0 |
| [Microsoft.Extensions.DependencyInjection](https://www.nuget.org/packages/Microsoft.Extensions.DependencyInjection) | runtime | Verified | 42 | 42 | 0 | 0 |
| [Microsoft.Extensions.Logging](https://www.nuget.org/packages/Microsoft.Extensions.Logging) | runtime | Verified | 21 | 21 | 0 | 0 |
| [Microsoft.AspNetCore.OpenApi](https://www.nuget.org/packages/Microsoft.AspNetCore.OpenApi) | aspnetcore | Verified | 48 | 48 | 0 | 0 |
| [Microsoft.AspNetCore.Components](https://www.nuget.org/packages/Microsoft.AspNetCore.Components) | aspnetcore | Verified | 217 | 217 | 0 | 0 |
| [Microsoft.AspNetCore.Components.Web](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.Web) | aspnetcore | Verified | 116 | 116 | 0 | 0 |
| [Microsoft.CodeAnalysis.CSharp](https://www.nuget.org/packages/Microsoft.CodeAnalysis.CSharp) | roslyn | Verified | 1149 | 1149 | 0 | 0 |
| [Microsoft.CodeAnalysis.Common](https://www.nuget.org/packages/Microsoft.CodeAnalysis.Common) | roslyn | Verified | 1042 | 1042 | 0 | 0 |
| [Microsoft.CodeAnalysis.Workspaces.Common](https://www.nuget.org/packages/Microsoft.CodeAnalysis.Workspaces.Common) | roslyn | Verified | 1567 | 1567 | 0 | 0 |
| [Microsoft.Extensions.AI.OpenAI](https://www.nuget.org/packages/Microsoft.Extensions.AI.OpenAI) | extensions | Verified | 24 | 24 | 0 | 0 |
| [Microsoft.Extensions.AI](https://www.nuget.org/packages/Microsoft.Extensions.AI) | extensions | Verified | 103 | 103 | 0 | 0 |
| [Microsoft.Extensions.Caching.Hybrid](https://www.nuget.org/packages/Microsoft.Extensions.Caching.Hybrid) | extensions | Verified | 30 | 30 | 0 | 0 |
This appears to affect multiple .NET repos, not just `dotnet/runtime` or `dotnet/extensions`.
### Control packages
This does not appear to be a dotnet-inspect artifact. Some packages verify with strict byte-for-byte SourceLink/PDB checksums without CR/LF normalization:
| Package | Group | Status | Verified | CR/LF normalized | Mismatched | Unverifiable |
| --- | --- | --- | ---: | ---: | ---: | ---: |
| [Markout](https://www.nuget.org/packages/Markout) | third-party | Verified | 70 | 0 | 0 | 0 |
| [MarkdownTable.Formatting](https://www.nuget.org/packages/MarkdownTable.Formatting) | third-party | Verified | 22 | 0 | 0 | 0 |
| [NuGetFetch](https://www.nuget.org/packages/NuGetFetch) | third-party | Verified | 12 | 0 | 0 | 0 |
For comparison, `Newtonsoft.Json` also shows the CR/LF-normalized pattern (`240/240` verified after normalization), so the issue is not unique to .NET-owned packages. The .NET-owned sample above is included because it demonstrates the same pattern across `runtime`, `aspnetcore`, `roslyn`, and `extensions` packages.
### Why this likely happens
The repo settings generally normalize text into Git but do not force `*.cs` working-tree files to LF:
- `dotnet/runtime`: `*.cs text diff=csharp`, no `eol=lf`
- `dotnet/extensions`: `*.cs text=auto diff=csharp`, no `eol=lf`
- `dotnet/aspnetcore`: `* text=auto`, no `*.cs eol=lf`
- `dotnet/roslyn`: `*.cs diff=csharp text`, no `eol=lf`
If official builds run from a Windows checkout or another CRLF working tree, the compiler records CRLF-based source checksums in the PDB. SourceLink then points to GitHub raw URLs for commit-pinned blobs, which are served as LF bytes.
### Impact
Tools performing strict SourceLink/PDB source verification will report checksum mismatches for official .NET packages, even though the logical source content matches after line-ending normalization.
`dotnet-inspect 0.9.0` now reports this explicitly as `CR/LF Mismatch`, but ideally official package PDB checksums would match the exact SourceLink bytes without normalization.
Contributor guide
Assessment
This issue has not been assessed yet.