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
---

Fix: Add DoS protection by limiting version string length in comparison methods.
5 changes: 5 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@
**Vulnerability:** The version comparison logic in `src/index.ts` failed to correctly parse version strings prefixed with `v` (e.g., `"v10.0.0"`). The `Number()` function would return `NaN` for segments like `"v10"`, leading to incorrect comparison results (often evaluating as equal due to `NaN` comparison behavior).
**Learning:** Naive number parsing of version strings can be dangerous. Standard semver libraries handle this, but custom implementations must be careful. Specifically, `NaN` in comparisons can lead to "fail open" scenarios where a lower version is considered "at least" a higher version because the check returns false for both `<` and `>`, falling through to equality or default cases.
**Prevention:** Always sanitize version strings (strip non-numeric prefixes) before parsing. When implementing custom version comparison, handle `NaN` explicitly or use a robust library. Ensure inputs are validated or normalized.

## 2026-01-15 - [DoS via Long Version Strings]
**Vulnerability:** The `compareTo` function lacked input length validation, exposing the application to Denial of Service (DoS) attacks via excessively long version strings that consume CPU and memory during parsing (e.g., `trim`, `split`).
**Learning:** Seemingly harmless string operations can become vectors for resource exhaustion if input size is unchecked.
**Prevention:** Enforce strict maximum length limits (e.g., `MAX_VERSION_LENGTH`) on all user-controlled inputs before processing them.
9 changes: 9 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import type { NodeVersion } from "./types.js";

export type { NodeVersion };

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

/**
* End-of-Life dates for Node.js major versions.
*/
Expand Down Expand Up @@ -52,6 +57,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;
}
Expand Down
6 changes: 6 additions & 0 deletions src/security.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,10 @@ describe("security fixes", () => {
const v = getVersion();
expect(v.isAtLeast("10.0.0")).toBe(true);
});

test("should reject excessively long version strings (DoS prevention)", () => {
const v = getVersion();
const longVersion = `1.${"0.".repeat(200)}1`;
expect(v.isAtLeast(longVersion)).toBe(false);
});
});