To build the exposure window for a package incident, find when the bad version was published, when it was removed or replaced, and which of your builds or machines installed it in between. Only installs inside that window can have pulled the bad code. Everything else is a name match, not an exposure.
Why the window matters
A news item says a package was compromised. Your lockfile shows the package. That does not yet say you are affected, because many incidents involve one or a few specific versions that were live for a limited time.
The window turns a vague worry into a bounded question: did anything we run fetch that version, between these two timestamps? If the answer is no for every build, laptop and image, you can say so with evidence.
Step 1: get the publish time of the bad version
For npm, the npm view command prints registry data about a package. The npm documentation shows how to read a single field and how to list the version history.
npm view some-package versions
npm view some-package time --json
The time field holds timestamps keyed by version. Run it first on a package you know, so you recognize the shape of the output before you rely on it.
For PyPI, the JSON API documents a route per project, and each file entry carries upload time fields.
curl -s https://pypi.org/pypi/some-project/json
Look at the upload_time_iso_8601 value on the files for the version in question. PyPI's docs also note that releases include hash digests, which helps later when you compare files.
Step 2: get the end of the window
The end is not always when the news broke. It is when the bad version stopped being installable, or when your own pin or mirror stopped serving it. Three different clocks apply:
- The registry removal or replacement time, from the maintainer or registry notice.
- The time your internal mirror or cache stopped serving it, which may be later.
- The time your build pipeline last ran a fresh install without a cached copy.
Use the latest of these as your conservative end. See the earlier post on what to do after the registry removes the package.
Step 3: match builds to the window
Now list every place something could have installed the package:
| Place | Evidence to check |
|---|---|
| CI runs | Job logs and the install step timestamp |
| Container images | Build date and the layer that ran the install |
| Developer laptops | Shell history and the lockfile modification time |
| Deployed artifacts | Release date and the lockfile commit it was built from |
A lockfile that predates the window and was installed with a frozen command normally holds the older version. A build that resolved fresh inside the window may not.
Step 4: write the result down
Record the bad version, the window start and end with time zone, the places you checked, and the verdict for each. Use UTC throughout so timestamps compare cleanly. If a build falls inside the window, move to your incident runbook for credential rotation and clean rebuilds.
A worked example
Say a maintainer notice states that version 5.0.2 of a package was published without authorization at 08:12 UTC on a Wednesday and removed at 11:30 UTC the same day. You run two services and a mobile build.
You collect what you need, one place at a time.
- Registry facts. You run the version and time commands and confirm that 5.0.2 appears with a publish timestamp near the notice. You save the output to the ticket.
- Mirror behavior. Your internal mirror caches packages for 24 hours. So the mirror could have served 5.0.2 until about 11:30 UTC on Thursday, or later if a cache refresh was delayed. Your conservative end is Thursday 11:30 UTC, not Wednesday.
- CI history. The web service ran seven builds between those times. Six restored a cached dependency folder keyed to the lockfile hash. One, at 09:05 UTC Wednesday, ran a fresh install because the lockfile changed.
- Lockfile check. The fresh install resolved the package through a range. The lockfile in that commit shows 5.0.1 because the lockfile pinned it, so the build installed 5.0.1 even though 5.0.2 was live.
- Mobile build. The mobile build has no lockfile for that package and resolves the newest version in range on every run. It ran at 10:40 UTC Wednesday. That one is inside the window and needs a closer look.
The verdict is not "we are affected" or "we are fine." It is a list: web service not exposed, evidence attached, mobile build possibly exposed, investigating. That is a calm, specific answer.
Common mistakes when building a window
| Mistake | Why it hurts | Better habit |
|---|---|---|
| Using the news time as the start | The version may have been live earlier | Use the registry publish time |
| Ending at the removal time | Mirrors and caches can keep serving it | End at the last time your own systems could serve it |
| Mixing time zones | A three hour error can move a build inside or outside the window | Convert everything to UTC first |
| Checking only the lockfile in main | A branch build may have used another lockfile | Check the commit each build actually used |
| Ignoring laptops | Developers install packages locally | Ask developers to run the version check and report |
What to do with builds inside the window
For each build or machine that falls inside the window and actually installed the bad version, treat it as exposed. That usually means: stop using the artifact, rebuild from a clean state with a known good version, rotate any credentials the build environment could read, and review what the build produced and shipped. The exact steps depend on what the bad version was known to do, which the primary source should describe. If the source does not say, assume the broader case until you learn otherwise.
For builds outside the window, note the evidence and close that line. Do not rotate credentials just because the package name matched. Over-reacting wastes time and trains people to ignore the next alert.
Keep the window in the ticket
A short block at the top of the ticket works well: version, publish time, end time, the clock you used, and one line per environment. Update it when you learn more. When a reviewer later asks how you scoped the incident, the answer is already written.
Try it yourself
Click through it at your own pace, or open it full screen. Look at how every component and version gets an entry, since that is the list you search first.
Safeguard's SBOM and dependency scanning keeps that inventory of packages and versions per project, so the lookup does not start from a blank page when news arrives.
Quick check
Is the window the same as the time the news was published?
No. The bad version may have been live well before the news, and may remain in your mirrors or caches after the registry removed it.
What if I cannot find the exact removal time?
Use the later of the registry notice and the last time your own systems could serve the version, and say the end is conservative.
Do I need the window if the package is only a transitive dependency?
Yes. A transitive dependency installs the same way as a direct one. Your lockfile or build logs show which version was resolved and when.
Sources
Last reviewed: 19 October 2026.