From 8f612bbaa17f21be210f3eac160c7207b67ac1e7 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 7 Jan 2026 18:36:12 +0000 Subject: [PATCH] feat(security): prevent DoS via long version strings - Add MAX_VERSION_LENGTH (256) constraint - Replace regex validation with faster charCodeAt loop - Add security test for long strings --- .changeset/sentinel_security_enhancement.md | 6 ++++++ src/index.ts | 18 ++++++++++++++++-- src/security.test.ts | 7 +++++++ 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 .changeset/sentinel_security_enhancement.md diff --git a/.changeset/sentinel_security_enhancement.md b/.changeset/sentinel_security_enhancement.md new file mode 100644 index 00000000..b912a04b --- /dev/null +++ b/.changeset/sentinel_security_enhancement.md @@ -0,0 +1,6 @@ +--- +"node-version": patch +--- + +Security: Enforce maximum version string length of 256 characters to prevent potential ReDoS/DoS vectors. +Security: Optimize version validation by replacing regex with character code checks. diff --git a/src/index.ts b/src/index.ts index 625ca808..1fc8710c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,6 +19,12 @@ export const EOL_DATES: Record = { "24": "2028-04-30", }; +/** + * Maximum allowed length for a version string. + * Prevents Denial of Service (DoS) attacks via excessively long strings. + */ +const MAX_VERSION_LENGTH = 256; + /** * Check if a major version is EOL. */ @@ -52,6 +58,10 @@ export const getVersion = (): NodeVersion => { * Compare the current node version with a target version string. */ const compareTo = (target: string): number => { + if (target.length > MAX_VERSION_LENGTH) { + return NaN; + } + if (target !== target.trim() || target.length === 0) { return NaN; } @@ -65,8 +75,12 @@ export const getVersion = (): NodeVersion => { const s2 = stripped.split("."); for (const segment of s2) { - if (segment === "" || !/^\d+$/.test(segment)) { - return NaN; + if (segment === "") return NaN; + for (let i = 0; i < segment.length; i++) { + const code = segment.charCodeAt(i); + if (code < 48 || code > 57) { + return NaN; + } } } diff --git a/src/security.test.ts b/src/security.test.ts index b5942fd7..92cc201d 100644 --- a/src/security.test.ts +++ b/src/security.test.ts @@ -63,4 +63,11 @@ describe("security fixes", () => { const v = getVersion(); expect(v.isAtLeast("10.0.0")).toBe(true); }); + + test("should fail for excessively long version strings", () => { + const v = getVersion(); + // 257 chars (MAX is 256) + const longString = "1".repeat(257); + expect(v.isAtLeast(longString)).toBe(false); + }); });