diff --git a/SECURITY.md b/SECURITY.md index 54a38a5..989d516 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -54,6 +54,27 @@ remedy for those is to upgrade. | 6.2.x | ✅ | | < 6.2 | ❌ | +### For how long + +There is no fixed end-of-support date. The current line targets **.NET 10** (`net10.0`, and EF Core +10 for the EF Core packages), and the intention is to keep it maintained for as long as Microsoft +supports that .NET release (.NET 10 is LTS, supported until November 2028), or until a new major of +this package supersedes it, whichever comes first — majors here are cheap, and a new one has never +meant the old one lost fixes before its successor shipped. If that intention changes, it is recorded +here before anywhere else. + +### If this project stops being maintained + +This is a single-maintainer project, and that is the honest continuity risk. The signal would be +unambiguous: the repository archived, the packages marked deprecated on nuget.org, and a note here. +Published versions stay on nuget.org regardless (a package can be unlisted, not removed), the code +is MIT-licensed, and the release path needs nothing but this repository — forking is the intended +continuity mechanism, not a fallback. + +Advisories are published as GitHub Security Advisories, which reach the GitHub Advisory Database +and from there `dotnet restore` (NuGetAudit): a consumer on an affected version sees a build +warning without subscribing to anything. + ## Where this package sits Useful context for judging impact, and for anyone doing supply-chain due diligence. diff --git a/test/CodoMetis.ValueRanges.Conventions.Tests/SecurityPolicyConventionTests.cs b/test/CodoMetis.ValueRanges.Conventions.Tests/SecurityPolicyConventionTests.cs index 4507445..d603472 100644 --- a/test/CodoMetis.ValueRanges.Conventions.Tests/SecurityPolicyConventionTests.cs +++ b/test/CodoMetis.ValueRanges.Conventions.Tests/SecurityPolicyConventionTests.cs @@ -22,12 +22,45 @@ public sealed class SecurityPolicyConventionTests private static readonly Regex UnsupportedRow = new(@"^\|\s*<\s*(?\d+)\.(?\d+)\s*\|\s*❌\s*\|", RegexOptions.Multiline); + /// "the current line targets **.NET 10**" — the number the horizon statement is anchored to. + private static readonly Regex TargetsDotNet = + new(@"targets \*\*\.NET (?\d+)\*\*", RegexOptions.None); + [TestMethod] public void SecurityPolicy_Exists() { Assert.IsTrue(SecurityPolicy.Exists, $"No SECURITY.md at the repository root ({SecurityPolicy.FullName})."); } + /// + /// The support horizon is phrased against the .NET major the packages target, and that number + /// lives in the core project's TargetFramework. When the TFM moves to net11.0 the sentence has + /// to move with it, or the policy promises support against a release the package no longer + /// targets. + /// + [TestMethod] + public void TheSupportHorizon_NamesTheTargetedDotNetMajor() + { + var text = File.ReadAllText(SecurityPolicy.FullName); + var match = TargetsDotNet.Match(text); + + Assert.IsTrue( + match.Success, + "SECURITY.md has no 'targets **.NET N**' sentence in its support horizon — a consumer doing " + + "due diligence needs to know which platform line the support intention is tied to."); + + var core = RepoLayout.PackableProjects.Single(project => project.PackageId == "CodoMetis.ValueRanges"); + var tfm = core.Property("TargetFramework") ?? ""; + var tfmMajor = Regex.Match(tfm, @"^net(?\d+)\.").Groups["major"].Value; + + Assert.IsFalse(string.IsNullOrEmpty(tfmMajor), $"Could not read a net. TargetFramework from {core.File.Name} (got '{tfm}')."); + + Assert.AreEqual( + tfmMajor, match.Groups["major"].Value, + $"SECURITY.md says the current line targets .NET {match.Groups["major"].Value}, but " + + $"{core.File.Name} targets {tfm}. Move the horizon statement with the TargetFramework."); + } + [TestMethod] public void TheSupportedVersionsTable_NamesTheShippedMinor() {