Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/sentinel-dos-fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"node-version": patch
---

Security: Fix potential DoS vulnerability by limiting maximum version string length to 256 characters.
10 changes: 10 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ export const EOL_DATES: Record<string, string> = {
"24": "2028-04-30",
};

/**
* Maximum allowed length for a version string to prevent DoS attacks.
*/
const MAX_VERSION_LENGTH = 256;

/**
* Check if a major version is EOL.
*/
Expand Down Expand Up @@ -52,6 +57,11 @@ export const getVersion = (): NodeVersion => {
* Compare the current node version with a target version string.
*/
const compareTo = (target: string): number => {
// Fail fast for excessively long strings to prevent DoS
if (target.length > MAX_VERSION_LENGTH) {
return NaN;
}

if (target !== target.trim() || target.length === 0) {
return NaN;
}
Expand Down
32 changes: 32 additions & 0 deletions src/security.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,36 @@ describe("security fixes", () => {
const v = getVersion();
expect(v.isAtLeast("10.0.0")).toBe(true);
});

describe("DoS prevention", () => {
test("should handle extremely long version strings gracefully", () => {
const v = getVersion();

// Generate a 1MB string
const hugeString = "1".repeat(1024 * 1024);

const start = performance.now();
const result = v.isAtLeast(hugeString);
const end = performance.now();

// Should return false (invalid version because it exceeds MAX_VERSION_LENGTH)
expect(result).toBe(false);

// Should execute quickly (under 50ms implies it didn't try to parse the whole thing)
expect(end - start).toBeLessThan(100);
});

test("should handle huge string with many dots", () => {
const v = getVersion();
const hugeDots = "1.".repeat(500000); // 1MB string with 500k dots

const start = performance.now();
const result = v.isAtLeast(hugeDots);
const end = performance.now();

expect(result).toBe(false);
// This is the critical check - without length limit this could be slow/OOM
expect(end - start).toBeLessThan(100);
});
});
});