Home/ Blog/ Security news/ Article
Blog · Security news

Perry's stdlib turned off JWT expiry checks. Logout stopped meaning anything.

CVE-2026-53776: Perry's bundled JWT helper hard-codes validate_exp = false, so expired and revoked tokens stay valid. Patch to 0.5.1166 and rotate signing keys.

Hourglass lying on its side with sand frozen evenly between both chambers

The dangerous flaws are rarely the loud ones. CVE-2026-53776 is a single inverted boolean inside a JWT helper, and it silently broke one of the few security controls every app assumes works: the ability to end a session. If you build on Perry, the native TypeScript compiler, and you lean on its bundled token helper, then your logout button has been decorative. Revoked tokens never died. Expired tokens never expired.

Here is the part that should worry you more than the bug itself: nothing looked broken. Valid tokens authenticated. Logins worked. Every functional test passed. The failure only shows up in the one test almost nobody writes, which is the negative one: hand the system a token that should be dead and confirm it gets rejected.

What CVE-2026-53776 actually is

Perry compiles TypeScript directly to native executables and ships its own standard library, including helpers for common tasks like verifying JSON Web Tokens. Inside that library, the verify_decode helper hard-codes validate_exp = false on every call. That one setting tells the verifier to skip the expiration check entirely. As long as the cryptographic signature is intact, the token is accepted, no matter how long ago it was supposed to die.

VulnCheck, the CNA that assigned the CVE, rates it 9.1 on CVSS 3.1 and 9.3 on CVSS 4.0, both critical. The weakness is classified as CWE-613, Insufficient Session Expiration. It affects every Perry release before 0.5.1166, which is the fix. The researcher credited on the VulnCheck advisory is Katriel Moses.

What makes the default so wrong is that it reverses the safe convention. Node's widely used jsonwebtoken library enforces the expiration claim by default; you have to opt out on purpose. Perry's helper did the opposite, opting everyone out with no flag to turn it back on. The project's own GitHub security advisory spells out the inversion.

Why a skipped expiry check is a critical bug

A bearer token is a key. Expiration is the part of the key that is supposed to crumble after a set time so a copied key stops working. Remove the expiry check and the key is permanent. Three things you thought you controlled stop working at once:

  • Logout. The user clicks log out, the client drops the token, but the token itself is still valid to the server. Anyone holding a copy stays in.
  • Forced session expiry. Short-lived tokens are a containment tool. If they never time out, a token stolen through a phishing page, a leaked log, or a shared device works indefinitely.
  • Administrative revocation. The classic incident-response move, kill a compromised user's sessions, does nothing here unless you have a separate server-side denylist.

So far there is no public report of exploitation in the wild, and the bug's EPSS score sits low. That is the window, not the all-clear. This is the kind of flaw that gets quietly abused after the fact, because a stolen token from months ago suddenly turns out to still work.

The pattern: convenience defaults that disable a control

Strip away the specifics and this is a familiar shape. A batteries-included library picks a default that makes the happy path smoother and turns a security check off to get there. We have seen the same move as permissive CORS by default, TLS clients shipping with certificate verification disabled, and debug modes left on in production. The common thread is that the insecure choice is invisible, because the visible behavior is identical to the secure one right up until someone abuses it.

The lesson for anyone shipping software is not Perry-specific. Do not trust a framework or runtime to enforce a security control just because the documentation implies it. Verify the control fires. For authentication, that means an explicit test that an expired token, and a revoked one, are both rejected. The auth stack is exactly where teams assume the platform has their back and stop checking, which is the same blind spot we wrote about in the Velvet Ant PAM and OpenSSH case.

What to do now

If you ship anything built with Perry, treat this as today's work, in this order:

  1. Upgrade to Perry 0.5.1166 or later and rebuild every affected service. The fix restores expiration validation.
  2. Rotate your JWT signing keys. This is the only lever that actually invalidates every token already in circulation, including ones an attacker may already hold. Until you patch, key rotation is your emergency logout-for-everyone, and you should treat it as one.
  3. Add a denylist or short-lived refresh model if you do not have one. Server-side session state is the backstop for exactly this failure, where the token itself cannot be trusted to expire.
  4. Write the negative test and keep it. An automated check that feeds the verifier an expired token and asserts a rejection would have caught this on day one. It belongs in your suite permanently, not just for this CVE.

The single inverted boolean is fixed. The habit that let it ship, trusting a default to enforce a security control nobody tested, is the thing to fix in your own code this week.

Frequently asked questions

What is CVE-2026-53776?

CVE-2026-53776 is a critical JWT validation flaw in Perry, a native TypeScript compiler. Its bundled stdlib helper sets validate_exp = false, so the verifier accepts expired bearer tokens as long as the signature is valid. VulnCheck rates it 9.1 (CVSS 3.1) and 9.3 (CVSS 4.0).

Which Perry versions are affected and where is the fix?

All Perry releases before 0.5.1166 are affected. The fix is version 0.5.1166, which restores JWT expiration validation. Upgrade and rebuild every service that uses Perry's stdlib token verification, then rotate your signing keys.

Why does skipping JWT expiration matter so much?

Expiration is what makes a stolen or revoked token stop working. Skip the check and logout, forced session timeouts, and administrative revocation all become ineffective. A token leaked through phishing, logs, or a shared device stays valid indefinitely until the signing key changes.

Is CVE-2026-53776 being exploited in the wild?

No public exploitation has been reported, and the bug's EPSS score is low. That is the window to act, not the all-clear. The risk is delayed abuse: a token captured months ago can still work, so patch and rotate keys before that happens.

How do I revoke tokens if I cannot patch immediately?

Rotate your JWT signing keys. Key rotation is the only step that invalidates every token already issued, including ones an attacker may hold. Pair it with a server-side denylist or short-lived refresh tokens so you can kill individual sessions without a full rotation.

How can I stop a bug like this from reaching production?

Write a negative test. Feed your verifier an expired token, and a revoked one, and assert that both are rejected. Functional tests only prove valid tokens work; they never catch a disabled expiry check. Keep that test in your suite permanently.

Ready to meet the Guardians?

Deploys fast - agentless for monitoring and cloud, a lightweight agent for deep endpoint security. Just Suriq, standing watch.