Reject negative index values in DefaultResolver.getIndex - #432
Conversation
getIndex returned a parsed negative subscript unvalidated, colliding with the -1 sentinel that means the property is not indexed, so callers branching on index >= 0 wrote the whole property instead of one element.
There was a problem hiding this comment.
Pull request overview
This pull request fixes an inconsistency in DefaultResolver.getIndex(...) by rejecting negative subscripts, preventing negative indices from colliding with the -1 “not indexed” sentinel and avoiding unintended fall-through to non-indexed property paths.
Changes:
- Reject negative indexed subscripts in
DefaultResolver.getIndexby throwingIllegalArgumentException. - Update
Resolver/DefaultResolverJavadoc to document that negative indices are invalid. - Extend
DefaultResolverTest.testGetIndexwith assertions covering negative indices and error messages.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/main/java/org/apache/commons/beanutils2/expression/DefaultResolver.java | Adds a negative-index guard to prevent -1 sentinel collisions and unintended non-indexed behavior. |
| src/main/java/org/apache/commons/beanutils2/expression/Resolver.java | Updates API documentation to reflect that negative indices are invalid. |
| src/test/java/org/apache/commons/beanutils2/expression/DefaultResolverTest.java | Adds regression tests ensuring negative indices throw IllegalArgumentException. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
garydgregory
left a comment
There was a problem hiding this comment.
@rootvector2
Java has am unchecked IndexOutOfBoundsException. Should it be used here? Are there other call sites that use IAE for this category of problems instead of IndexOutOfBoundsException?
|
The repo's convention is |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
getIndexparses the subscript withInteger.parseIntand returns it unvalidated, but the same method uses-1as its documented "not indexed" sentinel, sogetIndex("stringArray[-1]")is indistinguishable fromgetIndex("stringArray")andgetIndex("stringArray[-7]")hands the caller a negative subscript.PropertyUtilsBean.getIndexedProperty/setIndexedPropertyare unaffected because they have their ownindex < 0guard, but the three callers that branch onindex >= 0fall through to the non-indexed path and write the whole property instead of one element.BeanUtils.copyProperty(bean, "stringArray[-1]", "x")andLocaleBeanUtils.setProperty(bean, "stringArray[-1]", "x")both replace a five-elementString[]with["x"]and throw nothing, whileBeanUtils.setPropertyandPropertyUtils.setIndexedPropertyreject the same expression. Found while auditingDefaultResolveragainst its call sites after noticinggetIndexcan return a value that collides with its own sentinel.Indexes are zero-relative, so a negative subscript is rejected in
getIndexwith the sameIllegalArgumentExceptionit already throws for a non-numeric one. Keeping the check where the subscript is parsed covers all five call sites at once, andDefaultResolveris the onlyResolverimplementation. No existing test used a negative index; the new assertions inDefaultResolverTest.testGetIndexfail without the runtime change.mvn; that'smvnon the command line by itself.