Assembly Version Required
ID |
csharp.assembly_version_required |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Best Practice |
Language |
CSharp |
Tags |
assembly, best_practice, dotnet, versioning |
Description
Reports a .NET project that declares no assembly version: neither a version property in the project
file (or the Directory.Build.props it imports) nor an assembly-level AssemblyVersion attribute in
its sources. Every build of such a project produces an assembly stamped 0.0.0.0.
Rationale
The assembly version is part of an assembly’s identity. The runtime uses it to bind references, the GAC and side-by-side deployment use it to keep releases apart, strong naming includes it in the signed identity, and binding redirects are written in terms of it. Package managers and incident triage rely on it to answer "which build is running in production?".
When no version is declared the compiler emits 0.0.0.0 for every build. Two different releases then
have the same identity: a redirect cannot distinguish them, a partially updated deployment loads
whichever copy the probing rules find first, and a crash dump no longer identifies the build it came
from. The failure is silent — nothing warns at build time, and the consequence only appears once a
second version of the assembly exists somewhere.
Modern SDK-style projects declare the version in the project file and the SDK generates the attribute
during the build, so a project that carries no AssemblyVersion attribute in its sources is not
necessarily missing a version. Legacy projects declare the attribute by hand, conventionally in
Properties/AssemblyInfo.cs. Either place satisfies the rule; only a project with neither is
reported.
AssemblyFileVersion and AssemblyInformationalVersion do not satisfy it — they are informational
metadata shown in file properties and carry no part of the assembly identity.
// Properties/AssemblyInfo.cs of a project whose .csproj declares no version property
using System.Reflection;
[assembly: AssemblyTitle("Payments")]
[assembly: AssemblyFileVersion("1.4.0.0")] // FLAW — file version is not the assembly identity
// The same file, fixed: any of the spellings below satisfies the identity requirement.
[assembly: AssemblyVersion("1.4.0.0")] // OK
[assembly: AssemblyVersionAttribute("1.4.0.0")] // OK, explicit suffix
[assembly: System.Reflection.AssemblyVersion("1.4.0.0")] // OK, qualified
The project file is the other half of the verdict:
<!-- OK — the SDK generates [assembly: AssemblyVersion] from any of these -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Version>1.4.0</Version>
</PropertyGroup>
</Project>
The finding is reported once per project, on the project file itself, because an absent declaration has no place in the source to point at.
Remediation
Declare the version in the project file — <Version> is the usual choice, and it also seeds the
package and file versions. <VersionPrefix> (combined with a <VersionSuffix> supplied by the build)
and an explicit <AssemblyVersion> are equally accepted.
For a solution that versions all of its projects together, declare the property once in a
Directory.Build.props at the repository root; every project below it inherits the value, and the
rule consults that file too.
In a legacy project that has no version property, add [assembly: AssemblyVersion("1.0.0.0")] to
Properties/AssemblyInfo.cs.
Feed the value from the build pipeline rather than hand-editing it per release, so the version tracks the commit or tag being built.