July 30, 20262 min read

A certificate check that worked on my machine and was dead in production

Scan flags certificates signed with SHA-1. The check ran perfectly in development and silently never fired on the deployed site, because the two run different versions of Node.

Scan — the grader in the Sentinel suite — inspects TLS certificates, and one thing it flags is a SHA-1 signature. SHA-1 has practical collision attacks; a certificate signed with it should be treated as forgeable.

I wrote the check, tested it against a known-bad certificate, watched it fire, and moved on. It read the signature algorithm off Node’s X509Certificate:

const algo = new X509Certificate(cert.raw).signatureAlgorithm

That property exists. I confirmed it existed. It works.

It works on Node 26, which is what I develop on. Vercel runs my functions on Node 22, where signatureAlgorithm does not exist. On the runtime that actually serves every scan anyone runs, the property returns undefined, the check never matches, and a SHA-1 certificate passes as if it were fine.

The check was green in development and dead in production. Every test I had written passed. The tool would have told people their certificates were safe without ever having looked.

I only found it because I checked which Node version the deployed function runs, and then checked that specific property against that specific version. Not the docs — the actual runtime. It took ten minutes and I nearly didn’t bother.

The fix was to stop relying on a convenience API and read the signature algorithm out of the certificate’s raw DER bytes: walk into the structure, find the signature algorithm field, pull the OID, match it against the known SHA-1 identifiers. About forty lines, no dependency, and it behaves identically on both Node versions because it parses the bytes rather than asking the platform.

I verified it on Node 22 and Node 26 against the same bad certificate. Both flag it now. Development and production finally agree, which is the property I wanted and didn’t have.

Verify against the runtime you deploy to, not the one you develop on. A passing test suite proves your tests pass on your machine. It does not prove the code runs where it ships.

Interested in how this maps to your work?

If this kind of thinking fits a problem you're facing, let's talk it through.

Start a conversation EAT (UTC+3) · Typically responds within 24 hours