Issue-452 : Add support for annotation based input validation#903
Issue-452 : Add support for annotation based input validation#903aabashkin wants to merge 14 commits intoESAPI:developfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR implements annotation-based input validation support for ESAPI using the Java Bean Validation (JSR 380) API. It introduces 17 custom validation annotations that wrap existing ESAPI validation methods, enabling declarative validation of fields, parameters, and method return values.
Key changes:
- Created 17 custom validation annotations (ValidString, ValidCreditCard, ValidURI, etc.) with corresponding validator implementations
- Added ValidationUtil helper class for centralized error handling
- Comprehensive test suite covering all validation annotations
- Added javax.validation-api (2.0.1.Final) as a compile dependency and Hibernate Validator (6.2.5.Final) as a test dependency
Reviewed changes
Copilot reviewed 36 out of 37 changed files in this pull request and generated 23 comments.
Show a summary per file
| File | Description |
|---|---|
| ValidationAnnotationsTest.java | Comprehensive test suite covering all 17 validation annotations with positive and negative test cases |
| ValidationUtil.java | Utility class for converting ValidationErrorList to constraint violations |
| ValidURIValidator.java / ValidURI.java | URI validation annotation and validator |
| ValidStringValidator.java / ValidString.java | String validation annotation and validator with type and length constraints |
| ValidSafeHTMLValidator.java / ValidSafeHTML.java | Safe HTML validation annotation and validator |
| ValidRedirectLocationValidator.java / ValidRedirectLocation.java | Redirect location validation annotation and validator |
| ValidPrintableValidator.java / ValidPrintableStringValidator.java / ValidPrintable.java | Printable character validation for char arrays and strings |
| ValidNumberValidator.java / ValidNumber.java | Number validation annotation and validator with range constraints |
| ValidListItemValidator.java / ValidListItem.java | List item validation annotation and validator |
| ValidIntegerValidator.java / ValidInteger.java | Integer validation annotation and validator with range constraints |
| ValidHTTPRequestParameterSetValidator.java / ValidHTTPRequestParameterSet.java | HTTP request parameter set validation |
| ValidFileUploadValidator.java / ValidFileUpload.java | File upload validation with size and location constraints |
| ValidFileNameValidator.java / ValidFileName.java | File name validation with extension constraints |
| ValidFileContentValidator.java / ValidFileContent.java | File content validation with size constraints |
| ValidDoubleValidator.java / ValidDouble.java | Double validation annotation and validator with range constraints |
| ValidDirectoryPathValidator.java / ValidDirectoryPath.java | Directory path validation with parent directory constraints |
| ValidDateValidator.java / ValidDate.java | Date validation with format and locale support |
| ValidCreditCardValidator.java / ValidCreditCard.java | Credit card validation using Luhn algorithm |
| DefaultValidator.java | Minor formatting change (trailing whitespace) |
| pom.xml | Added javax.validation-api, hibernate-validator, and javax.el dependencies |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (input == null) { | ||
| return true; | ||
| } |
There was a problem hiding this comment.
The null check returns true even when allowNull is false. This means null values will always pass validation regardless of the allowNull setting. The validator should return allowNull when input is null to properly respect the allowNull configuration.
There was a problem hiding this comment.
We need to make a design decision here:
- null values are accepted unless
@NotNullis present. Since the user would already be using annotations, it could make sense to assume that they would leverage an existing, often used constraint.
OR - Require
allowNulleverywhere
I was leaning towards 1 initially but I'm starting to consider 2 more seriously. Thoughts?
There was a problem hiding this comment.
@aabashkin - So for the delay; my wife has been having medical problems (still is, in fact), so I've had to prioritize that.
I have been looking at this though a bit though and thinking about it deeply. The potential problem that I foresee with approach 1 is that, what happens if someone uses the @NotNull annotation, but then also specifies allowNull as true? How do we resolve that case? I'm not can be resolved at compile-time, but even at runtime, it seems as though may some RuntimeException or perhaps even some sort of custom subclass of java.lang.Error should be thrown.
The other way might be eliminate allowNull as an explicit parameter in all these annotations where it's used and simply treat it as true unless @NotNull is present, but I'm not sure how that would work because I'm not sure if annotation order matters or not. I would think it would, and in that case, if someone did something like this:
@NotNull
@ValidNumber(context = "num", minValue = 1, maxValue = 10000)
String queryParmNum;versus an ordering like this:
@ValidNumber(context = "num", minValue = 1, maxValue = 10000)
@NotNull
String queryParmNum;my intuition tells me that that difference may be significant from the way code gets generated,
Somewhat related to all of this is the testing. For example, a quick perusal of src/test/java/org/owasp/esapi/reference/validation/annotations/ValidationAnnotationsTest.java seems to indicate that only "sunny-day" tests are provided. In ESAPI, we need to test failure cases as well...ideally all of them, but at least enough to get code-coverage of 70%. Yes, there are some older, rarely used components that don't have anywhere near that. I hope to address that by getting genAI to create some additional test cases, but that's probably still 5-6 months out. But I have no objection to you using genAI with something like Copilot or Claude Code, etc. as long as the prompts are saved and included in GitHub somewhere and the generated code is maintainable by a human.
Lastly, the Javadoc documentation needs some enhancing. I haven't looked through it in detail yet, but I did notice the total absence of package.html in all your new packages. (We don't need to have that for src/test stuff, but do want it for src/main.)
I will try to look at this more closely in the following weeks, but as I mention, family comes first, so no promise of how soon. Thanks for your understanding in this matter.
| @Override | ||
| public boolean isValid(String input, ConstraintValidatorContext constraintValidatorContext) { | ||
| if (input == null) { | ||
| return true; |
There was a problem hiding this comment.
The null check returns true even when allowNull is false. This means null values will always pass validation regardless of the allowNull setting. The validator should return allowNull when input is null to properly respect the allowNull configuration.
| return true; | |
| return allowNull; |
|
|
||
| public class ValidationUtil { | ||
|
|
||
| private ValidationUtil(){} |
There was a problem hiding this comment.
Missing space after closing brace. The constructor should have a space before the opening brace for consistency with Java formatting conventions.
| if (input == null) { | ||
| return true; | ||
| } |
There was a problem hiding this comment.
The null check returns true even when allowNull is false. This means null values will always pass validation regardless of the allowNull setting. The validator should return allowNull when input is null to properly respect the allowNull configuration.
| @Override | ||
| public boolean isValid(String input, ConstraintValidatorContext constraintValidatorContext) { | ||
| if (input == null) { | ||
| return true; |
There was a problem hiding this comment.
The null check returns true even when allowNull is false. This means null values will always pass validation regardless of the allowNull setting. The validator should return allowNull when input is null to properly respect the allowNull configuration.
| return true; | |
| return allowNull; |
| @Override | ||
| public boolean isValid(String input, ConstraintValidatorContext constraintValidatorContext) { | ||
| if (input == null) { | ||
| return true; |
There was a problem hiding this comment.
The null check returns true even when allowNull is false. This means null values will always pass validation regardless of the allowNull setting. The validator should return allowNull when input is null to properly respect the allowNull configuration.
| return true; | |
| return allowNull; |
| @Override | ||
| public boolean isValid(String input, ConstraintValidatorContext constraintValidatorContext) { | ||
| if (input == null) { | ||
| return true; |
There was a problem hiding this comment.
The null check returns true even when allowNull is false. This means null values will always pass validation regardless of the allowNull setting. The validator should return allowNull when input is null to properly respect the allowNull configuration.
| return true; | |
| return allowNull; |
| @Override | ||
| public boolean isValid(String input, ConstraintValidatorContext constraintValidatorContext) { | ||
| if (input == null) { | ||
| return true; |
There was a problem hiding this comment.
The null check returns true even when allowNull is false. This means null values will always pass validation regardless of the allowNull setting. The validator should return allowNull when input is null to properly respect the allowNull configuration.
| return true; | |
| return allowNull; |
| @Override | ||
| public boolean isValid(String input, ConstraintValidatorContext constraintValidatorContext) { | ||
| if (input == null) { | ||
| return true; |
There was a problem hiding this comment.
The null check returns true even when allowNull is false. This means null values will always pass validation regardless of the allowNull setting. The validator should return allowNull when input is null to properly respect the allowNull configuration.
| return true; | |
| return allowNull; |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@kwwall please review at your convenience, thanks.