Prove the Advisory Is Unreachable

Two HIGH advisories landed on a direct production dependency, and there was no upgrade to take. The audit tool's suggested fix would have rolled a framework back to defend a path that did not exist. This is what I did instead, and the general version of it.

No Upgrade

Both advisories covered every version of an image-sizing library, up to and including the latest one published. Nobody upstream has fixed it, so I wasn't behind, and an override had nothing to point at. Getting rid of the library meant getting rid of the framework that pulls it in.

That left two honest options: accept it blind, or find out whether it was reachable.

Reachability by Import Trace, Not by Name

One file in the framework's server bundle imports the library, a metadata-route build helper. The name says build time, but I didn't want to take a filename's word for it, so I traced the imports from the production start command through the CLI to the server entry. Everything that loads from there comes to 42 modules, and that file isn't one of them.

One catch. The CLI does statically import a module that reaches the helper, so the library gets loaded into the production process. It's loaded and never called. That's weaker than unreachable, and I wrote it down as the weaker claim.

The Load-Bearing Check, By Execution

Where an image-sizing library would actually matter is the image-optimization endpoint that takes a URL, since that's where an attacker would put a crafted file. So I ran the production build and hit it.

A remote URL with a crafted extension: 400. A protocol-relative URL: 400. A path traversal toward the environment file: 400. A legitimate local image: 200, streamed unmodified.

The endpoint refuses everything that would reach the vulnerable code, and the app itself never feeds it anything. Intake photos arrive as base64 in a JSON body and go straight to the inference provider. They're never decoded locally or written to disk.

What Would Change This

If someone adds a metadata image to the app, like an icon or a social-preview image, the build-time helper would run. Even then the exposure would only be at build time. I wrote that next to the decision, so whoever adds the next icon knows what they're reopening.

I deliberately skipped an image-host allowlist in the framework config. It would defend a path that doesn't exist here. It would also make the hardening pass look more thorough while making the config lie about what the app does.

The Other Direction

The same week, in another repo, the audit tool's fix for a HIGH advisory on a database driver was to install an older major version of that driver. I tried it and reverted it. The older version drags in an HTTP client with about ten HIGH advisories of its own, so the audit count doesn't get better and the driver goes back four years.

The live exposure was low. The driver is optional and only loads when something asks for it, and the vulnerable path is its installer, not its query path. So I carry the advisory, with the reasoning in the roadmap: don't take the suggested fix, here's why, and here's what would change the answer.

The same trade-off came up in a second repo with the same driver. It got turned down there too, on its own, and written down there too. That's the part worth keeping. An advisory you've decided to carry needs a written reason, or every future hygiene pass will argue it again, automated ones included, and one of those runs next week.

The Form

Is there an upgrade? If not, what's the reachable path from the production entry point, traced and not guessed from filenames? What input does the app actually feed it? Hit the endpoint that would matter, with the payloads that would matter, and record the status codes. Say what would change the answer. Don't add a defense for a path that isn't there.

Then write it all down where the next audit will read it, because the next audit won't remember you did this, and the next audit is on Tuesday.

-- Justin Higgins. Software Engineer, Midwest. Traced the import closure, hit the endpoint, and refused the allowlist.


Companion pieces: Read It Cold - what a clone can actually do. Verify the Artifact, Not the Process - by request, not by assumption.

Reactions, disagreements, war stories: jchigg2000.dev@gmail.com