From b2df8e40b3194349a044de078752f488ddc8fcc3 Mon Sep 17 00:00:00 2001 From: Priyanshubhartistm Date: Wed, 22 Jul 2026 20:11:52 +0530 Subject: [PATCH 1/2] fix: reject expiration timestamp 0 and accept far-future timestamps in getEventExpiration() --- .changeset/fix-event-expiration-validation.md | 5 +++++ src/utils/event.ts | 2 +- test/unit/utils/event.spec.ts | 10 ++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 .changeset/fix-event-expiration-validation.md diff --git a/.changeset/fix-event-expiration-validation.md b/.changeset/fix-event-expiration-validation.md new file mode 100644 index 00000000..14609437 --- /dev/null +++ b/.changeset/fix-event-expiration-validation.md @@ -0,0 +1,5 @@ +--- +"nostream": patch +--- + +fix: reject expiration timestamp 0 and accept safe-integer timestamps beyond year 2287 in getEventExpiration() diff --git a/src/utils/event.ts b/src/utils/event.ts index 78b012b0..9f23ee1f 100644 --- a/src/utils/event.ts +++ b/src/utils/event.ts @@ -253,7 +253,7 @@ export const getEventExpiration = (event: Event): number | undefined => { const expirationTime = Number(rawExpirationTime) - if (Number.isSafeInteger(expirationTime) && Math.log10(expirationTime) < 10) { + if (Number.isSafeInteger(expirationTime) && expirationTime > 0) { return expirationTime } } diff --git a/test/unit/utils/event.spec.ts b/test/unit/utils/event.spec.ts index 53cc0f6c..9c747ac2 100644 --- a/test/unit/utils/event.spec.ts +++ b/test/unit/utils/event.spec.ts @@ -691,6 +691,16 @@ describe('NIP-40', () => { event.tags = [['expiration', 'a']] expect(getEventExpiration(event)).to.be.undefined }) + + it('returns false if expiration is 0', () => { + event.tags = [['expiration', '0']] + expect(getEventExpiration(event)).to.be.undefined + }) + + it('returns true if expiration is a safe integer beyond year 2287', () => { + event.tags = [['expiration', '10000000000']] + expect(getEventExpiration(event)).to.equal(10000000000) + }) }) describe('isExpiredEvent', () => { From 7b7761a5132040cc594ba030951cbae384ef70a2 Mon Sep 17 00:00:00 2001 From: Priyanshubhartistm Date: Fri, 24 Jul 2026 20:34:54 +0530 Subject: [PATCH 2/2] fix: reject millisecond-scale expiration timestamps while keeping far-future support --- .changeset/fix-event-expiration-validation.md | 2 +- src/utils/event.ts | 7 ++++++- test/unit/utils/event.spec.ts | 10 ++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/.changeset/fix-event-expiration-validation.md b/.changeset/fix-event-expiration-validation.md index 14609437..649dab5b 100644 --- a/.changeset/fix-event-expiration-validation.md +++ b/.changeset/fix-event-expiration-validation.md @@ -2,4 +2,4 @@ "nostream": patch --- -fix: reject expiration timestamp 0 and accept safe-integer timestamps beyond year 2287 in getEventExpiration() +fix: reject expiration timestamp 0 and millisecond-scale values, and accept safe-integer second-based timestamps up to year 9999 in getEventExpiration() diff --git a/src/utils/event.ts b/src/utils/event.ts index 9f23ee1f..3c67d68d 100644 --- a/src/utils/event.ts +++ b/src/utils/event.ts @@ -245,6 +245,11 @@ export const isExpiredEvent = (event: Event): boolean => { return expirationTime <= now } +// 9999-12-31T23:59:59Z as Unix seconds. Millisecond-scale timestamps for any date +// from 2001 onward are at least 13 digits, well past this ceiling, so this rejects +// accidental millisecond values without capping legitimate far-future expirations. +const MAX_EXPIRATION_TIME = 253402300799 + export const getEventExpiration = (event: Event): number | undefined => { const [, rawExpirationTime] = event.tags.find((tag) => tag.length >= 2 && tag[0] === EventTags.Expiration) ?? [] if (!rawExpirationTime) { @@ -253,7 +258,7 @@ export const getEventExpiration = (event: Event): number | undefined => { const expirationTime = Number(rawExpirationTime) - if (Number.isSafeInteger(expirationTime) && expirationTime > 0) { + if (Number.isSafeInteger(expirationTime) && expirationTime > 0 && expirationTime <= MAX_EXPIRATION_TIME) { return expirationTime } } diff --git a/test/unit/utils/event.spec.ts b/test/unit/utils/event.spec.ts index 9c747ac2..e0d5852b 100644 --- a/test/unit/utils/event.spec.ts +++ b/test/unit/utils/event.spec.ts @@ -701,6 +701,16 @@ describe('NIP-40', () => { event.tags = [['expiration', '10000000000']] expect(getEventExpiration(event)).to.equal(10000000000) }) + + it('returns true if expiration is the maximum representable Unix seconds timestamp', () => { + event.tags = [['expiration', '253402300799']] + expect(getEventExpiration(event)).to.equal(253402300799) + }) + + it('returns false if expiration looks like a millisecond-scale timestamp', () => { + event.tags = [['expiration', '1700000000000']] + expect(getEventExpiration(event)).to.be.undefined + }) }) describe('isExpiredEvent', () => {