When you publish a security advisory for your own library, what must it state about affected and fixed versions?
answer
- tools read the data, not the prose
- identity is ecosystem plus name
- introduced version and first fixed version
- one range per maintained release line
- half-open: fixed version is excluded
basics
~20 sName the package identity in its ecosystem, the version where the vulnerable code was introduced, and the first fixed version on every release line you maintain. Ranges must be structured data, not prose like 'all earlier versions'.
solid answer
~50 sDownstream tooling never reads your prose; it compares the versions it resolved against your ranges, so the structured part is the advisory. It needs three things. First, package identity in the ecosystem's own terms - ecosystem plus name, ideally a purl like `pkg:npm/example-lib` - because the same bare name exists in several registries. Second, an `introduced` version and a `fixed` version per affected branch, evaluated with that ecosystem's version ordering rather than string comparison. Third, one range per maintained release line: a fix backported to 1.9.4 and 2.3.1 has two first-fixed versions, and a single range either misses 1.x consumers or falsely condemns the patched ones. A line you will not patch still belongs in the advisory, marked affected with no fix - omitting it reads to tooling as 'not affected'. Add the severity vector, references and any aliases, and publish once the fixed builds are downloadable.
go deeper
Be ready to say what an advisory must carry beyond a description: the package and its ecosystem, the version the flaw was introduced in, and the first fixed version. Know that the fixed version itself is not affected.
Explain why the range is evaluated with the ecosystem's version ordering, what a half-open interval means in practice, and why a backported fix needs one range per release line rather than one span.
Show the operational judgment: publishing only after the fixed builds land, stating the status of lines you will not patch, correcting a wrong range in place instead of issuing a duplicate entry.
Own the standard your organisation publishes to - who reviews ranges before they go out, how corrections are handled without eroding trust, and why sloppy ranges cost you downstream credibility long after the bug is forgotten.
## An advisory is data before it is prose When you publish a security advisory for a library you maintain, almost nobody reads it. What reads it is tooling: an ecosystem advisory database ingests your entry, and a consumer's dependency check compares the versions it resolved against your ranges. If the structured part is wrong or missing, the prose is decoration - the consumers who needed the alert never get one, and consumers who did not need it get woken at 3am. So the real question is: what does a machine need in order to decide whether one installed version is affected? Four answers - identity, boundaries, per-line coverage, and the remedy. ## Identity: which package, in which ecosystem A package name alone is ambiguous. The same string exists in several registries, and forks and renames multiply it. The advisory must bind the flaw to an ecosystem plus a name - ideally a package URL (purl) such as `pkg:nuget/Example.Auth`, which encodes type, namespace and name in one string a resolver can compare against what it actually installed. If your project ships under more than one coordinate - a scoped and an unscoped name, a module path that changed at a major version, a copy vendored under a different name - each coordinate needs its own entry. Tooling matches the coordinate it resolved; a coordinate you did not list is a consumer you did not alert. ## Boundaries: introduced and fixed, as a half-open interval The affected set is expressed as a range with events. The common form is an `introduced` version and a `fixed` version, and the interval is half-open: the introduced version IS affected, the fixed version is NOT. `introduced: 0` means from the first release ever published. Where a line was never fixed you can bound it with a last-affected version instead. Where an ecosystem's versions cannot be ordered reliably, advisory formats let you enumerate affected versions explicitly rather than describe a range. Two details bite people. First, comparison uses the ecosystem's version ordering, not string comparison: `1.10.0` is greater than `1.9.0` numerically and smaller lexically, and prerelease precedence, epochs and normalisation rules differ per ecosystem - so declare which ordering applies (SemVer where the ecosystem is SemVer, the ecosystem's own ordering otherwise). Second, `introduced` is a claim about when the vulnerable code appeared, which is usually not the first release; reaching for zero out of caution condemns versions that never carried the bug. ## Per-line coverage: one range per maintained branch If you maintain 1.x and 2.x and backported the fix, there are two first-fixed versions and one range cannot express that. A single range from 0 to 2.3.1 marks every 1.x release affected including the patched 1.9.4 - every consumer on the fixed 1.x build gets a false alert and learns to ignore you. A single range starting at 2.0.0 leaves every 1.x consumer silent and exposed. The correct shape is one affected entry per maintained line, each with its own introduced and fixed events. A line you have decided not to patch still belongs in the advisory: mark it affected with no fixed version and say in the text that the remedy is moving to a supported line. Which lines get a backport is a release decision; what the advisory owes you is that every line's status is stated, including the ones with no good answer. ## The remedy and the rest Beyond ranges, an advisory carries: a summary and detail text a human can act on; the severity published as a vector string rather than a bare number, so a reader can see which assumption drives it; references to the fix commit or release notes; the identifier plus any aliases, so a consumer who already has this flaw under a different ID does not act on it twice; and credit for the reporter, which costs nothing and is how you keep getting reports. Timing matters as much as content. An advisory naming 2.3.1 before 2.3.1 is downloadable tells attackers exactly where to look and leaves defenders nothing to do. ## When you get it wrong You will. Ranges get corrected constantly - the flaw predates the version you named, or a branch was missed, or a coordinate was forgotten. Advisory formats carry a modified timestamp precisely because entries are living documents. Correct the entry in place; do not open a second one. A duplicate splits the ecosystem's view of one flaw and both copies end up half-trusted. The habit worth building: before publishing, take your own draft and evaluate it against two real installed versions - one you believe is affected, one you believe is not - and confirm the range actually says what you meant.
- Your fix shipped in both 1.9.4 and 2.3.1. How many ranges does the advisory need?Two - one per maintained line, each with its own introduced and fixed events. A single range spanning both lines either flags the already-patched 1.9.4 as vulnerable or leaves every 1.x consumer with no alert at all. Two ranges cost nothing and are the only shape that is true for both branches.
- What do you publish for a release line you have decided never to fix?List it as affected with no fixed version - bounded by a last-affected version if the line is closed - and state in the text that the remedy is upgrading to a supported line. Leaving it out entirely is worse than saying 'no fix': tooling reads absence as 'not affected' and those consumers are never told.
- Why does the ecosystem's version ordering matter when the advisory is just two version strings?Because a consumer's tool has to decide whether 1.10.0 falls inside a range ending at 1.9.0, and string comparison gets that backwards. Prerelease precedence, epochs and version normalisation also differ by ecosystem. Declaring the ordering makes the range mean one thing rather than whatever comparator the reader happens to use.
A recall notice that says 'some of our chairs' helps nobody. The serial-number range is the notice; the paragraph explaining the wobble is context.
saying these in an interview costs you the question
- Saying 'all versions before the fix' and stopping there
- Publishing prose only, with no structured version range
- Using one range to cover every maintained release line
- Assuming versions compare correctly as plain strings
- Omitting the ecosystem, leaving the package name ambiguous
- Publishing the advisory before the fixed build is downloadable