Skip to content

Add input limits to prevent DoS from untrusted version strings#20

Merged
andrew merged 1 commit intomainfrom
security-input-limits
Apr 13, 2026
Merged

Add input limits to prevent DoS from untrusted version strings#20
andrew merged 1 commit intomainfrom
security-input-limits

Conversation

@andrew
Copy link
Copy Markdown
Owner

@andrew andrew commented Apr 13, 2026

Bounds input length and constraint count at parser entry points so malformed or hostile input fails fast instead of consuming unbounded memory or CPU.

The three caches in Version, Constraint, and Parser use raw input strings as hash keys and evict by entry count, not byte size. An attacker comparing 2000 unique multi-megabyte version strings could pin gigabytes of memory before eviction starts. Version::MAX_LENGTH (256) at construction means oversized input raises before reaching any cache, capping key memory at entries * 256.

parse_constraints runs exclusions.each { |v| range = range.exclude(v) } where each exclude splits an interval and reconstructs the range with sort and merge. Interval count grows from 1 to N over N exclusions, doing O(N² log N) work. Input like vers:npm/!=1|!=2|...|!=5000 ran for several seconds. Parser::MAX_CONSTRAINTS (64) caps the split, and the split itself uses a limit arg so we never materialise thousands of substrings.

Parser::MAX_INPUT_LENGTH (2048) at parse and parse_native bounds total string work before any regex or split runs.

The bare rescue in the maven union parser now catches only ArgumentError, letting Interrupt and NoMemoryError propagate.

Limits sit well above real-world inputs: longest version strings in the wild are Maven snapshots around 60 chars, complex SBOM ranges rarely exceed 20 constraints. Error messages report the offending length but not the string itself to avoid echoing payloads into logs.

test/test_security.rb covers each limit and asserts that inputs near the limits still parse in well under a second.

Bounds input length and constraint count at parser entry points so
malformed or hostile input fails fast instead of consuming unbounded
memory or CPU.

- Version::MAX_LENGTH (256) caps version strings at construction and
  cache lookup, bounding cache key memory at entries * MAX_LENGTH
- Parser::MAX_INPUT_LENGTH (2048) caps range strings at parse/parse_native
- Parser::MAX_CONSTRAINTS (64) caps |-separated and ||-separated
  constraint counts, preventing the O(n^2 log n) exclusion loop from
  being driven into multi-second runtime
- Narrows bare rescue in maven union parser to ArgumentError so it no
  longer swallows Interrupt or NoMemoryError

Tests in test/test_security.rb cover each limit plus regression checks
that legitimate inputs near the limits still parse fast.
@andrew andrew merged commit 5fa9531 into main Apr 13, 2026
2 checks passed
@andrew andrew deleted the security-input-limits branch April 13, 2026 08:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant