skip to content

Remediation & Upkeep

Patching is a cadence problem, not a one-off: batched bumps, an auto-merge policy, and packages with no upstream fix left. Interviewers probe how you keep an estate current without drowning a team.

on this pageshow

explore

questions

20

A scanner flags a vulnerable transitive dependency and a fixed version exists — why can you not always just upgrade to it?

level: juniorimportance: must knowfreq 66%

answer

  1. you did not choose that version
  2. something between you and the fix
  3. an intermediate declares the constraint
  4. patch published, graph refuses it
  5. four exits: wait, override, exclude, upstream

basics

~20 s

You do not choose that version. An intermediate library in your graph declares which version of the flagged package to pull, so until that library raises or widens its constraint, your build keeps resolving the vulnerable one.

solid answer

~50 s

The flagged package is not one I declare — it arrives because a library I do declare requires it, and that library's own constraint decides which version resolves. If it asks for an exact version, or a range that stops below the fix, adding the patched version to my manifest does not by itself change what gets installed. That is a blocked upgrade path: the patch is published, but the graph will not take it. From there I have four real exits: wait for the intermediate to release a version that allows the fix, override the resolved version myself, exclude the vulnerable one and declare the fixed version directly, or open a pull request upstream so the intermediate raises its own constraint. The first three change my build, the last one changes the actual cause. Each of the middle two buys speed at the cost of running the intermediate against a version it never tested with.

code

json · 8 lines
json
{
  "root": { "name": "payments-api", "requires": { "internal-sdk": ">=3.2,<4" } },
  "resolved": {
    "internal-sdk": { "version": "3.2.4", "requires": { "serial-lib": "==2.4.1" } },
    "serial-lib":   { "version": "2.4.1", "advisory": "fixed in 2.4.2" }
  }
  ...
}

go deeper

for a junior

Be ready to say why a transitive package is still your responsibility, and that the version installed is decided by the constraint of the library that requires it, not by your manifest alone.

for a middle

Explain the difference between an exact pin and a bounded range as blockers, and name the exits — wait, override, exclude and declare, upstream pull request — with the compatibility cost of each.

for a senior

Show that you verify remediation against the resolved graph and the built artifact, not the manifest, and that you pair any local workaround with an upstream change so it can be removed.

for a principal

Own the estate view: how many services sit behind the same blocking constraint, whether you fund an upstream contribution once instead of patching many builds, and what you commit to externally while the block stands.

## What a blocked upgrade path is Most vulnerability findings are boring to fix: an advisory names a package and a fixed version, you bump the version you declare, the finding clears. A **blocked upgrade path** is the case where that does not work. The flagged package is *transitive* — you never named it. It is present because something you did name requires it, and the version that ends up installed is chosen by that intermediate's declared constraint, not by your intent. So there are two different sentences that sound the same and are not: - "No fix exists" — the maintainer has published nothing, the project is dead, the flaw is unpatched. - "A fix exists and my graph will not take it" — the patched release is sitting in the registry, and something between you and it refuses to allow it. This question is about the second one, and the distinction matters because the responses are completely different. ## Why your own manifest is not the last word Every ecosystem resolves a graph, not a list. Your project declares a handful of direct requirements; each of those declares its own, and so on. When a package appears on more than one path, the build tool has to pick one version, and the constraints along those paths are inputs to that choice. If the only path to the flagged package runs through a library that says "I need exactly `2.4.1`", then `2.4.1` is what the resolver is being told to honour. An exact pin is the sharpest version of this — one `==` in someone else's metadata can hold an entire estate one patch release below a fix. Ranges block too, just more quietly: an upper bound like `<3` blocks a fix that first shipped in `3.0.1`, and a fix backported only to a newer minor is out of reach for anyone stuck on the old one. ## The four exits, and what each costs **1. Wait for the intermediate to release.** The cleanest outcome and often the fastest one, if a release is days away. Cost: you are carrying a known finding, and "the maintainer said soon" is not a plan you can hand an auditor without a date. **2. Override the resolved version.** Most ecosystems offer some facility to say "whatever the graph asks for, install this" — a replace directive, an overrides block, a constraints file. Cost: you are now running the intermediate library against a version of its own dependency that it never declared and never tested against. If the delta is a patch release this is usually safe; if it crosses a major version, you have swapped a security finding for a possible runtime failure, and the failure can be silent until a rarely used code path executes. **3. Exclude the vulnerable package and declare the fixed one directly.** Functionally similar to an override, but it also makes you the declaring owner of that package, which is useful — the dependency is now visible in your manifest rather than buried. Cost: the same compatibility gamble, plus a durability problem, because an exclusion is written against a coordinate and a path and both can change under you. **4. Open a pull request upstream.** The only exit that fixes the cause rather than your copy of the symptom, and the only one that helps everyone else stuck behind the same constraint. Cost: latency you do not control. These are not mutually exclusive, and the mature answer usually pairs the last one with one of the first three: ship an override now with an expiry, land the upstream change so you can delete it later. ## What a green scan actually proves After any of these, the scanner going quiet proves one thing only: the artifact it read no longer lists the vulnerable version. It does not prove the patched code is what loads. Confirm the fix by looking at the resolved graph or lockfile the build actually produced, and by scanning the built artifact rather than only the manifest — an override that failed to apply, a second path you did not exclude, or a vendored copy inside another package all produce a green manifest and a vulnerable runtime. ## The framing that reads as senior A candidate who says "just add it to the manifest" has not met a real graph. A candidate who says "we would have to fork it" has jumped past three cheaper options. The answer interviewers want names the blocker precisely — *which* dependency's *which* constraint holds the version down — and then picks an exit with its cost stated out loud.

  • Does it help that the fixed release is only one patch version above the exact pin the intermediate declares?
    It helps with compatibility, not with the block. A one-patch delta means an override is very unlikely to break the intermediate, so the technical risk of forcing it is small. But an exact pin still refuses the fix on its own, so you still have to override, exclude, or get the pin changed upstream. The small delta also makes the upstream pull request trivially easy to review, which is worth saying out loud.
  • How would you tell a blocked upgrade apart from a finding where no fix exists at all?
    Read the advisory's fixed-version field and check the registry. If a patched release is published, the problem is in my graph and the responses are override, exclude, or upstream. If nothing is published — the project is archived, or the maintainer has not shipped — then no amount of graph surgery helps and it becomes a different problem entirely. Conflating the two wastes days arguing with a resolver over a version that does not exist.
  • Your scanner reports the finding as resolved after you change your manifest. What do you check before believing it?
    The resolved graph, not the manifest. I look at the lockfile or dependency tree the build actually produced and confirm the fixed version is the only one present, then scan the built artifact rather than the source declaration. Overrides can fail to apply, a second path can reintroduce the old version, and a scanner pointed at a manifest reports intent rather than what ships.

It is like being told your building must use a newer, safer lock, while the door was fitted by a contractor whose spec sheet says one exact model. You can drill the door yourself, or get the contractor to update the spec.

saying these in an interview costs you the question

  • Says adding the fixed version to your manifest always wins
  • Assumes a green scanner means the fixed code is loaded
  • Treats a blocked upgrade as needing an immediate fork
  • Says transitive dependencies are the library author's problem
  • Confuses a blocked fix with no fix existing at all

context

open as a page

What does mitigating a vulnerable dependency in place mean, and when do you do it instead of patching?

level: juniorimportance: must knowfreq 62%

basics

~20 s

Mitigating in place removes the attack path without changing the dependency version: disable the vulnerable feature by config, or block the network route that reaches it. Use it when the patch will take longer than the exposure is tolerable.

open as a page

A dependency has a known vulnerability and no fixed version exists. What are your options?

level: juniorimportance: must knowfreq 68%

basics

~20 s

Four options: delete the dependency, replace it with a maintained equivalent, patch it yourself and own that patch forever, or contain it behind a control that blocks the vulnerable path. Deletion is often cheapest and rarely even costed.

open as a page

Why does scanning container images not replace SCA on manifests and lockfiles?

level: juniorimportance: must knowfreq 70%

basics

~20 s

They build different inventories. An image scanner lists what it can find inside a built image, mostly OS packages installed by the distro. Manifest and lockfile SCA reads the dependency graph your build resolved. Each misses what the other finds.

open as a page

Why are 60 unread dependency-update pull requests a security problem, not just backlog?

level: juniorimportance: must knowfreq 68%

basics

~20 s

An unread update queue means known fixes sit unapplied, and the one bump carrying a security fix looks exactly like the 59 routine ones. It also grows upgrade distance, so the next urgent patch becomes a large, risky jump.

open as a page

A critical advisory hits 180 services you already know are affected. How do you order the fixes in the first hour?

level: middleimportance: must knowfreq 71%

basics

~20 s

Order by exposure, not by score. Services that are internet-reachable and feed attacker-controlled input into the vulnerable code path go first, then internally reachable ones, then batch and build-only workloads. The score is identical across all 180, so it cannot rank them.

open as a page

What test evidence justifies letting a dependency bump merge without a human reviewing it?

level: seniorimportance: must knowfreq 60%

basics

~20 s

Only evidence that actually exercises the changed dependency. A green build proves the covered paths still work and nothing about the rest, so the unattended-merge rule must stay narrow enough that your tests cover the risk it takes.

open as a page

You excluded a vulnerable transitive package and declared the fixed version directly — what can silently undo that fix?

level: middleimportance: should knowfreq 41%

basics

~20 s

Upgrading the intermediate library. An exclusion is written against a specific coordinate on a specific path, so when that library changes how it pulls the package, the exclusion stops matching, the vulnerable version returns, and nothing in the build fails.

open as a page

When you backport a security fix onto an abandoned dependency, what are you committing to?

level: middleimportance: should knowfreq 44%

basics

~20 s

You become that package's maintainer. You own review of a patch nobody upstream will check, every future flaw in the same code with no advisory to warn you, and the gap between what your build actually ships and what your inventory claims it ships.

open as a page

Dependency scanning can run in the editor, a pull request, the registry or at runtime — what does each placement buy?

level: middleimportance: should knowfreq 55%

basics

~20 s

Each placement trades speed against truth. Editor and pull-request scans read what you declare and give feedback before a change lands. Registry and runtime scans read the built artifact and what is deployed, which is the inventory that matches production.

open as a page

Why does a dependency update policy need both a scheduled sweep and advisory-driven upgrades?

level: middleimportance: should knowfreq 54%

basics

~20 s

They have different triggers and catch different things. A scheduled sweep is time-driven and keeps you close to upstream, including fixes that never get an advisory. An advisory-driven upgrade is event-driven, jumps the queue, and runs on somebody else's clock.

open as a page

The fix requires an intermediate library to raise its own constraint — how do you ship while that upstream pull request waits?

level: seniorimportance: should knowfreq 49%

basics

~20 s

Run two tracks. Land a local override now, scoped and tested, with an owner and a removal trigger tied to the upstream change. In parallel push the pull request itself: minimal diff, tests, the advisory cited, escalated through the project's security contact rather than a public thread.

open as a page

During a live critical advisory, how do you choose between freezing releases, pinning, and rolling forward?

level: seniorimportance: should knowfreq 49%

basics

~20 s

Freezing stops other changes so the fix is the only thing moving; pinning holds the dependency at an exact known-good version so a rebuild cannot drift; rolling forward ships the fix through the normal train. Choose by exposure and by how much unrelated change the fix drags along.

open as a page

An end-of-life PDF parsing library in a claims-intake pipeline has an unfixable memory-corruption flaw. What compensating controls do you deploy?

level: seniorimportance: should knowfreq 52%

basics

~20 s

Cut the attack path and the blast radius: pre-filter or transcode documents before the parser sees them, run it unprivileged, network-denied and resource-capped one document at a time, alert on crashes, and give the control an owner and a review date.

open as a page

Renovate keeps every dependency current — does that replace a vulnerability scanner?

level: seniorimportance: should knowfreq 45%

basics

~20 s

No. An update bot is a remediation channel, not a detector: it proposes version changes where a newer version exists and it can rewrite a manifest. Up-to-date and not-vulnerable are overlapping sets, not the same set.

open as a page

How do you prove an emergency dependency patch actually reached production everywhere?

level: seniorimportance: nice to knowfreq 37%

basics

~20 s

A merged pull request and a closed ticket prove intent, not state. Proof is a chain: the fixed version inside a built artifact, that artifact identified by digest, that digest running in every environment, and a re-scan of the running inventory returning no matches.

open as a page

Your SCA reports zero dependencies for a statically linked Go binary — is that good news?

level: seniorimportance: nice to knowfreq 32%

basics

~20 s

No. A manifest-based scanner found nothing because no lockfile ships beside the binary. The result describes the scanner's input, not the artifact. Go binaries embed their module list, so an artifact-level scan still enumerates them.

open as a page

What does batching twenty dependency bumps into one change cost you when it breaks production?

level: seniorimportance: nice to knowfreq 38%

basics

~20 s

You lose the one-to-one mapping between a symptom and a single change. Bisecting now happens inside the batch rather than across history, and reverting is all-or-nothing, so undoing one bad member also undoes nineteen good ones and reopens their exposure.

open as a page

A vendor-managed runtime pins a vulnerable library and the vendor's fix is two quarters out — how do you decide what to do?

level: principalimportance: nice to knowfreq 28%

basics

~20 s

Establish real exposure first, then treat it as a decision that leaves engineering: isolate or move the workload if the exposure is real, otherwise accept the risk formally with a named owner, a written justification, an expiry and a review trigger — and press the vendor through the contract.

open as a page

A defunct vendor's component is vulnerable on 200,000 customer machines with no update channel. What do you do?

level: principalimportance: nice to knowfreq 27%

basics

~20 s

Split the problem. Replace the component in the next release so new installs are clean. Then decide separately what you owe the machines you cannot reach: a customer advisory with manual steps, a costly new update path, or accepted risk with a declared end of support.

open as a page