From fea568c3c42efcc4841391d76ae6c01f8fc1352f Mon Sep 17 00:00:00 2001 From: Arc Studio Agent Date: Wed, 7 Oct 2026 19:04:27 +0000 Subject: [PATCH 1/2] fix(vc): treat unparseable expirationDate as expired (fail-closed) Previously isExpired() returned false when expirationDate could not be parsed, silently allowing a credential with a malformed expiry to pass every expiry check. This is inconsistent with the fail-closed approach used elsewhere in the verification pipeline (resolveStatusListCredential throws on an unreadable expirationDate rather than ignoring it). Return true instead so a credential whose expiry cannot be established is rejected, not accepted. --- packages/vc/src/verification/is-expired.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/vc/src/verification/is-expired.ts b/packages/vc/src/verification/is-expired.ts index 1d66ac8e..818ab2bd 100644 --- a/packages/vc/src/verification/is-expired.ts +++ b/packages/vc/src/verification/is-expired.ts @@ -4,7 +4,15 @@ import type { W3CCredential } from "../types" * Check if a credential is expired * * @param credential - The {@link W3CCredential} to check - * @returns `true` if the credential is expired, `false` otherwise + * @returns `true` if the credential is expired or has an unparseable + * `expirationDate`, `false` if it is verifiably not yet expired + * + * An unparseable date is treated as expired rather than "not expired": + * a credential whose expiry cannot be established should not pass an + * expiry check, consistent with the fail-closed approach used elsewhere + * in this verification pipeline (e.g. `resolveStatusListCredential` + * throws on an unreadable `expirationDate` instead of treating it as + * absent). */ export function isExpired(credential: W3CCredential): boolean { if (!credential.expirationDate) { @@ -14,8 +22,9 @@ export function isExpired(credential: W3CCredential): boolean { const expirationDate = new Date(credential.expirationDate) if (isNaN(expirationDate.getTime())) { - // Expiration date is invalid, so we consider the credential not expired - return false + // An unreadable expiry date cannot be verified — treat as expired + // (fail-closed) rather than silently allowing the credential through. + return true } return expirationDate < new Date() From 1331714a865d1950a379c2cd63393cc86b438925 Mon Sep 17 00:00:00 2001 From: Arc Studio Agent Date: Wed, 7 Oct 2026 19:37:34 +0000 Subject: [PATCH 2/2] test(vc): update invalid date test to expect fail-closed behavior --- packages/vc/src/verification/is-expired.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vc/src/verification/is-expired.test.ts b/packages/vc/src/verification/is-expired.test.ts index ac456e47..50ae03d1 100644 --- a/packages/vc/src/verification/is-expired.test.ts +++ b/packages/vc/src/verification/is-expired.test.ts @@ -47,9 +47,9 @@ describe("isExpired", () => { expect(isExpired(credential)).toBe(false) }) - it("handles invalid date strings gracefully", () => { + it("treats invalid date strings as expired (fail-closed)", () => { const credential = buildCredential("invalid-date") - expect(isExpired(credential)).toBe(false) + expect(isExpired(credential)).toBe(true) }) })