From 13bb0874158b02044fd01d49241c05390440e7f4 Mon Sep 17 00:00:00 2001 From: Anton Abashkin Date: Mon, 26 Dec 2022 23:36:32 +0700 Subject: [PATCH 01/14] first commit --- pom.xml | 18 ++++ .../validation/annotations/ValidString.java | 43 +++++++++ .../annotations/ValidStringValidator.java | 42 +++++++++ .../StringValidationAnnotationTest.java | 91 +++++++++++++++++++ 4 files changed, 194 insertions(+) create mode 100644 src/main/java/org/owasp/esapi/reference/validation/annotations/ValidString.java create mode 100644 src/main/java/org/owasp/esapi/reference/validation/annotations/ValidStringValidator.java create mode 100644 src/test/java/org/owasp/esapi/reference/validation/annotations/StringValidationAnnotationTest.java diff --git a/pom.xml b/pom.xml index 8e4b926e4..d5c5c1f2a 100644 --- a/pom.xml +++ b/pom.xml @@ -168,6 +168,11 @@ + + javax.validation + validation-api + 2.0.1.Final + xom xom @@ -398,6 +403,19 @@ ${version.jmh} test + + org.hibernate.validator + hibernate-validator + 6.2.5.Final + test + + + org.glassfish + javax.el + 3.0.0 + test + + diff --git a/src/main/java/org/owasp/esapi/reference/validation/annotations/ValidString.java b/src/main/java/org/owasp/esapi/reference/validation/annotations/ValidString.java new file mode 100644 index 000000000..217b42018 --- /dev/null +++ b/src/main/java/org/owasp/esapi/reference/validation/annotations/ValidString.java @@ -0,0 +1,43 @@ +package org.owasp.esapi.reference.validation.annotations; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.Payload; + + +@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) +@Retention(RUNTIME) +@Constraint(validatedBy = ValidStringValidator.class) +@Documented +public @interface ValidString { + + String message() default ""; + + Class[] groups() default {}; + + Class[] payload() default {}; + + String context(); + + String type() default "SafeString"; + + int maxLength(); + + boolean allowNull() default false; + + boolean canonicalize() default true; + +} + + diff --git a/src/main/java/org/owasp/esapi/reference/validation/annotations/ValidStringValidator.java b/src/main/java/org/owasp/esapi/reference/validation/annotations/ValidStringValidator.java new file mode 100644 index 000000000..eebfaec64 --- /dev/null +++ b/src/main/java/org/owasp/esapi/reference/validation/annotations/ValidStringValidator.java @@ -0,0 +1,42 @@ +package org.owasp.esapi.reference.validation.annotations; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +import org.owasp.esapi.ValidationErrorList; +import org.owasp.esapi.errors.ValidationException; +import org.owasp.esapi.reference.DefaultValidator; + + +public class ValidStringValidator implements ConstraintValidator{ + + private String context; + private String type; + private int maxLength; + private boolean allowNull; + private boolean canonicalize; + + @Override + public void initialize(ValidString validString) { + context = validString.context(); + type = validString.type(); + maxLength = validString.maxLength(); + allowNull = validString.allowNull(); + canonicalize = validString.canonicalize(); + } + + @Override + public boolean isValid(String input, ConstraintValidatorContext constraintContext) { + ValidationErrorList errorList = new ValidationErrorList(); + boolean valid = DefaultValidator.getInstance().isValidInput(context, input, type, maxLength, allowNull, canonicalize, errorList); + + if(!valid){ + for (ValidationException vex : errorList.errors()) { + String errorMessage = vex.getLogMessage(); + constraintContext.buildConstraintViolationWithTemplate(errorMessage).addConstraintViolation(); + } + } + + return valid; + } +} \ No newline at end of file diff --git a/src/test/java/org/owasp/esapi/reference/validation/annotations/StringValidationAnnotationTest.java b/src/test/java/org/owasp/esapi/reference/validation/annotations/StringValidationAnnotationTest.java new file mode 100644 index 000000000..dde6f25bc --- /dev/null +++ b/src/test/java/org/owasp/esapi/reference/validation/annotations/StringValidationAnnotationTest.java @@ -0,0 +1,91 @@ +package org.owasp.esapi.reference.validation.annotations; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.Valid; +import javax.validation.Validation; +import javax.validation.Validator; + +import org.junit.Before; +import org.junit.Test; + +import junit.framework.TestCase; + + +public class StringValidationAnnotationTest extends TestCase { + + private String validString = "John"; + + private String invalidStringType = "John"; + assertViolations(bean, 1); + } + + @Test + public void testValidURI() { + UriBean bean = new UriBean("http://example.com"); + assertViolations(bean, 0); + bean.value = "javascript:alert(1)"; + assertViolations(bean, 1); + } + + @Test + public void testValidHTTPRequestParameterSet() { + MockHttpServletRequest validRequest = new MockHttpServletRequest(); + validRequest.addParameter("required", "value"); + validRequest.addParameter("optional", "value"); + HttpRequestBean bean = new HttpRequestBean(validRequest); + assertViolations(bean, 0); + + MockHttpServletRequest invalidRequest = new MockHttpServletRequest(); + bean.request = invalidRequest; + assertViolations(bean, 1); + } + + private boolean isSystemDriveC() { + String systemDrive = System.getenv("SystemDrive"); + return systemDrive == null || "C:".equalsIgnoreCase(systemDrive); + } + + private void assertViolations(T bean, int expected) { + Set> violations = validator.validate(bean); + assertEquals(expected, violations.size()); + } + + private static class CreditCardBean { + @ValidCreditCard(context = "cc", allowNull = false) + private String number; + + CreditCardBean(String number) { + this.number = number; + } + } + + private static class DateBean { + @ValidDate(context = "date", dateStyle = DateFormat.SHORT, locale = "en-US", allowNull = false) + private String value; + + DateBean(String value) { + this.value = value; + } + } + + private static class DirectoryPathWindowsBean { + @ValidDirectoryPath(context = "dir", parent = WINDOWS_ROOT, allowNull = false) + private String path; + + DirectoryPathWindowsBean(String path) { + this.path = path; + } + } + + private static class DirectoryPathUnixBean { + @ValidDirectoryPath(context = "dir", parent = UNIX_ROOT, allowNull = false) + private String path; + + DirectoryPathUnixBean(String path) { + this.path = path; + } + } + + private static class DoubleBean { + @ValidDouble(context = "double", minValue = 0, maxValue = 20, allowNull = false) + private String value; + + DoubleBean(String value) { + this.value = value; + } + } + + private static class FileContentBean { + @ValidFileContent(context = "content", maxBytes = 5, allowNull = false) + private byte[] content; + + FileContentBean(byte[] content) { + this.content = content; + } + } + + private static class FileNameBean { + @ValidFileName(context = "filename", allowedExtensions = {".txt"}, allowNull = false) + private String name; + + FileNameBean(String name) { + this.name = name; + } + } + + private static class FileUploadWindowsBean { + @ValidFileUpload( + context = "upload", + directoryPath = WINDOWS_ROOT, + fileName = "test.txt", + parent = WINDOWS_ROOT, + maxBytes = 5, + allowNull = false + ) + private byte[] content; + + FileUploadWindowsBean(byte[] content) { + this.content = content; + } + } + + private static class FileUploadUnixBean { + @ValidFileUpload( + context = "upload", + directoryPath = UNIX_ROOT, + fileName = "test.txt", + parent = UNIX_ROOT, + maxBytes = 5, + allowNull = false + ) + private byte[] content; + + FileUploadUnixBean(byte[] content) { + this.content = content; + } + } + + private static class IntegerBean { + @ValidInteger(context = "int", minValue = 0, maxValue = 10, allowNull = false) + private String value; + + IntegerBean(String value) { + this.value = value; + } + } + + private static class ListItemBean { + @ValidListItem(context = "item", list = {"red", "green"}, allowNull = false) + private String value; + + ListItemBean(String value) { + this.value = value; + } + } + + private static class NumberBean { + @ValidNumber(context = "num", minValue = 1, maxValue = 100, allowNull = false) + private String value; + + NumberBean(String value) { + this.value = value; + } + } + + private static class PrintableCharsBean { + @ValidPrintable(context = "print", maxLength = 10, allowNull = false) + private char[] value; + + PrintableCharsBean(char[] value) { + this.value = value; + } + } + + private static class PrintableStringBean { + @ValidPrintable(context = "print", maxLength = 10, allowNull = false) + private String value; + + PrintableStringBean(String value) { + this.value = value; + } + } + + private static class RedirectBean { + @ValidRedirectLocation(context = "redirect", allowNull = false) + private String value; + + RedirectBean(String value) { + this.value = value; + } + } + + private static class SafeHtmlBean { + @ValidSafeHTML(context = "safehtml", maxLength = 200, allowNull = false) + private String value; + + SafeHtmlBean(String value) { + this.value = value; + } + } + + private static class UriBean { + @ValidURI(context = "uri", allowNull = false) + private String value; + + UriBean(String value) { + this.value = value; + } + } + + private static class HttpRequestBean { + @ValidHTTPRequestParameterSet( + context = "params", + requiredNames = {"required"}, + optionalNames = {"optional"}, + allowNull = false + ) + private HttpServletRequest request; + + HttpRequestBean(HttpServletRequest request) { + this.request = request; + } + } +} From 794119bdd145e7580aa4952c14b701d88c510f50 Mon Sep 17 00:00:00 2001 From: Anton Abashkin Date: Mon, 5 Jan 2026 16:05:28 -0500 Subject: [PATCH 13/14] Test consolidation --- .../StringValidationAnnotationTest.java | 91 ------------------- .../ValidationAnnotationsTest.java | 48 ++++++++++ 2 files changed, 48 insertions(+), 91 deletions(-) delete mode 100644 src/test/java/org/owasp/esapi/reference/validation/annotations/StringValidationAnnotationTest.java diff --git a/src/test/java/org/owasp/esapi/reference/validation/annotations/StringValidationAnnotationTest.java b/src/test/java/org/owasp/esapi/reference/validation/annotations/StringValidationAnnotationTest.java deleted file mode 100644 index f22bfc2a9..000000000 --- a/src/test/java/org/owasp/esapi/reference/validation/annotations/StringValidationAnnotationTest.java +++ /dev/null @@ -1,91 +0,0 @@ -package org.owasp.esapi.reference.validation.annotations; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -import javax.validation.ConstraintViolation; -import javax.validation.Valid; -import javax.validation.Validation; -import javax.validation.Validator; - -import org.junit.Before; -import org.junit.Test; - -import junit.framework.TestCase; - - -public class StringValidationAnnotationTest extends TestCase { - - private String validString = "John"; - - private String invalidStringType = "John