Skip to content

Commit 7883f8c

Browse files
committed
fix(validation): resolve version leading zeros and token expiration bugs 🐛
- Enhanced regex pattern for proper version format validation - Fixed token expiration logic for very small expiration times - Fixed version validation to reject leading zeros (e.g., 01.0.0) - Improved time comparison to use <= instead of < for accurate expiration - Updated security policy version support range to 1.x.x - 2.x.x
1 parent dfc4004 commit 7883f8c

File tree

7 files changed

+23
-15
lines changed

7 files changed

+23
-15
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
---
99

10+
## [1.4.3] - 2025-09-06
11+
12+
### Fixed
13+
- **Version validation**: Fixed leading zeros bug (e.g., "01.0.0" now properly rejected)
14+
- **Token expiration**: Fixed decimal expireIn bug for very small expiration times
15+
16+
---
17+
1018
## [1.4.2] - 2025-09-06
1119

1220
### Added

SECURITY.md

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@ We release patches for security vulnerabilities in the following versions:
66

77
| Version | Supported |
88
| ------- | ------------------ |
9-
| 1.3.x ||
10-
| 1.2.x ||
11-
| 1.1.x ||
12-
| 1.0.x ||
9+
| 1.x.x - 2.x.x ||
1310
| < 1.0 ||
1411

1512
## Security Features
@@ -105,4 +102,4 @@ For security-related questions or concerns:
105102
---
106103

107104
**Last Updated**: September 6, 2025
108-
**Version**: 1.3.0
105+
**Version**: 1.4.3

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@neabyte/secure-jwt",
33
"description": "Secure JWT with AES-256-GCM & ChaCha20-Poly1305 encryption, built-in caching, tamper detection, and TypeScript support",
4-
"version": "1.4.2",
4+
"version": "1.4.3",
55
"type": "module",
66
"main": "./dist/index.js",
77
"types": "./dist/index.d.ts",

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ export default class SecureJWT {
127127
try {
128128
ErrorHandler.validateData(data)
129129
const now = Math.floor(Date.now() / 1000)
130-
const exp = now + Math.floor(this.#expireInMs / 1000)
130+
const exp = now + Math.max(1, Math.floor(this.#expireInMs / 1000))
131131
const maxExp = now + 365 * 24 * 60 * 60
132132
ErrorHandler.validateExpiration(exp, maxExp)
133133
const payload: PayloadData = {

src/utils/ErrorHandler.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,10 @@ export class ErrorHandler {
142142
if (version.length === 0) {
143143
throw new ValidationError(getErrorMessage('VERSION_CANNOT_BE_EMPTY'))
144144
}
145-
if (!/^\d+\.\d+\.\d+$/.test(version)) {
145+
if (
146+
!/^(0|[1-9]\d{0,8})\.(0|[1-9]\d{0,8})\.(0|[1-9]\d{0,8})$/.test(version) ||
147+
version === '0.0.0'
148+
) {
146149
throw new ValidationError(getErrorMessage('VERSION_INVALID_FORMAT'))
147150
}
148151
}
@@ -214,7 +217,7 @@ export class ErrorHandler {
214217
*/
215218
static checkTokenExpiration(exp: number): void {
216219
const now = Math.floor(Date.now() / 1000)
217-
if (exp < now) {
220+
if (exp <= now) {
218221
throw new TokenExpiredError(getErrorMessage('TOKEN_EXPIRED'))
219222
}
220223
}

src/utils/TimeParser.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ export function timeToMs(timeUnit: TimeUnit): number {
3636
* @throws {TimeFormatError} When time string format is invalid or value is negative
3737
*/
3838
export function parseTimeString(timeString: string): TimeUnit {
39-
const timeRegex = /^(\d+(?:\.\d+)?)(ms|s|m|h|d|M|y)$/
39+
const timeRegex = /^(\d+)(ms|s|m|h|d|M|y)$/
4040
const match = timeRegex.exec(timeString)
4141
if (!match || match.length < 3 || match[1] === undefined || match[2] === undefined) {
4242
throw new TimeFormatError(errorMessages.TIME_FORMAT_INVALID)
4343
}
44-
const value = parseFloat(match[1])
44+
const value = parseInt(match[1], 10)
4545
const unit = match[2] as TimeUnit['unit']
4646
if (value <= 0) {
4747
throw new TimeFormatError(errorMessages.TIME_VALUE_NEGATIVE)

tests/TimeParser.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ describe('TimeParser', () => {
3232
})
3333

3434
it('should handle decimal values', () => {
35-
expect(timeToMs({ value: 1.5, unit: 'm' })).toBe(90000)
35+
expect(timeToMs({ value: 1, unit: 'm' })).toBe(60000)
3636
})
3737

3838
it('should throw TimeFormatError for unsupported unit', () => {
@@ -51,9 +51,9 @@ describe('TimeParser', () => {
5151
expect(parseTimeString('1y')).toEqual({ value: 1, unit: 'y' })
5252
})
5353

54-
it('should parse decimal values', () => {
55-
expect(parseTimeString('1.5m')).toEqual({ value: 1.5, unit: 'm' })
56-
expect(parseTimeString('2.5h')).toEqual({ value: 2.5, unit: 'h' })
54+
it('should reject decimal values', () => {
55+
expect(() => parseTimeString('1.5m')).toThrow(TimeFormatError)
56+
expect(() => parseTimeString('2.5h')).toThrow(TimeFormatError)
5757
})
5858

5959
it('should throw TimeFormatError for invalid format', () => {

0 commit comments

Comments
 (0)