5.2 KiB
Normalized Version Rule Recipes
Status: 2025-10-29
This guide captures the minimum wiring required for connectors and Merge coordination tasks to finish the normalized-version rollout that unblocks FEEDMERGE-COORD-02-9xx.
1. Quick-start checklist
- Ensure your mapper already emits
AffectedPackage.VersionRanges(SemVer, NEVRA, EVR). If you only have vendor/product strings, capture the raw range text before trimming so it can feed the helper. - Call
SemVerRangeRuleBuilder.BuildNormalizedRules(rawRange, patchedVersion, provenance)for each range and place the result inAffectedPackage.NormalizedVersions. - Set a provenance note in the format
connector:{advisoryId}:{index}so Merge can differentiate connector-provided rules from canonical fallbacks. - Verify with
dotnet testthat the connector snapshot fixtures now include thenormalizedVersionsarray and update fixtures by setting the connector-specificUPDATE_*_FIXTURES=1environment variable. - Tail Merge logs (or the test output) for the new warning
Normalized version rules missing for {AdvisoryKey}; an empty warning stream means the connector/merge artefacts are ready to close FEEDMERGE-COORD-02-901/902.
2. Code snippet: SemVer connector (CCCS/Cisco/ICS-CISA)
using StellaOps.Concelier.Normalization.SemVer;
private static IReadOnlyList<AffectedPackage> BuildPackages(MyDto dto, DateTimeOffset recordedAt)
{
var packages = new List<AffectedPackage>();
foreach (var entry in dto.AffectedEntries.Select((value, index) => (value, index)))
{
var rangeText = entry.value.Range?.Trim();
var patched = entry.value.FixedVersion;
var provenance = $"{MyConnectorPlugin.SourceName}:{dto.AdvisoryId}:{entry.index}";
var normalizedRules = SemVerRangeRuleBuilder.BuildNormalizedRules(rangeText, patched, provenance);
var primitives = SemVerRangeRuleBuilder.Build(rangeText, patched, provenance)
.Select(result => result.Primitive.ToAffectedVersionRange(provenance))
.ToArray();
packages.Add(new AffectedPackage(
AffectedPackageTypes.SemVer,
entry.value.PackageId,
versionRanges: primitives,
normalizedVersions: normalizedRules,
provenance: new[]
{
new AdvisoryProvenance(
MyConnectorPlugin.SourceName,
"package",
entry.value.PackageId,
recordedAt,
new[] { ProvenanceFieldMasks.AffectedPackages })
}));
}
return packages;
}
A few notes:
- If you already have
SemVerPrimitiveinstances, call.ToNormalizedVersionRule(provenance)on each primitive instead of rebuilding from raw strings. - Use
SemVerRangeRuleBuilder.BuildNormalizedRuleswhen the connector only tracks raw range text plus an optional fixed/patched version. - For products that encode ranges like
"ExampleOS 4.12 - 4.14", run a small regex to peel off the version substring (see §3) and use the same provenance note when emitting the rule and the original range primitive.
3. Parsing helper for trailing version phrases
Many of the overdue connectors store affected products as natural-language phrases. The following helper normalises common patterns (1.2 - 1.4, <= 3.5, Version 7.2 and later).
private static string? TryExtractRangeSuffix(string productString)
{
if (string.IsNullOrWhiteSpace(productString))
{
return null;
}
var match = Regex.Match(productString, "(?<range>(?:<=?|>=?)?\s*\d+(?:\.\d+){0,2}(?:\s*-\s*\d+(?:\.\d+){0,2})?)", RegexOptions.CultureInvariant);
return match.Success ? match.Groups["range"].Value.Trim() : null;
}
Once you extract the range fragment, feed it to SemVerRangeRuleBuilder.BuildNormalizedRules(range, null, provenance). Keep the original product string as-is so operators can still see the descriptive text.
4. Merge dashboard hygiene
- Run
dotnet build src/Concelier/__Libraries/StellaOps.Concelier.Merge/StellaOps.Concelier.Merge.csprojafter wiring a connector to confirm no warnings appear. - Merge counter tag pairs to watch in Grafana/CI logs:
concelier.merge.normalized_rules{package_type="npm"}– increases once the connector emits normalized arrays.concelier.merge.normalized_rules_missing{package_type="vendor"}– should trend to zero once rollout completes.
- The Merge service now logs
Normalized version rules missing for {AdvisoryKey}; sources=...; packageTypes=...when a connector still needs to supply normalized rules. Use this as the acceptance gate for FEEDMERGE-COORD-02-901/902.
5. Documentation touchpoints
- Update the connector
TASKS.mdentry with the date you flipped on normalized rules and note the provenance format you chose. - Record any locale-specific parsing (e.g., German
bis) in the connector README so future contributors can regenerate fixtures confidently. - When opening the PR, include
dotnet testoutput covering the connector tests so reviewers see the normalized array diff.
Once each connector follows the steps above, we can mark FEEDCONN-CCCS-02-009, FEEDCONN-CISCO-02-009, FEEDCONN-CERTBUND-02-010, FEEDCONN-ICSCISA-02-012, and the FEEDMERGE-COORD-02-90x tasks as resolved.