CVE-2020-36635: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: A vulnerability was found in OpenMRS Appointment Scheduling Module up to 1.12.x. It has been classified as problematic. This affects the function validateFieldName of the file api/src/main/java/org/openmrs/module/appointmentscheduling/validator/AppointmentTypeValidator.java. The manipulation leads to cross site scripting. It is possible to initiate the attack remotely. Upgrading to version 1.13.0 is able to address this issue. The name of the patch is 34213c3f6ea22df427573076fb62744694f601d8. It is recommended to upgrade the affected component. The associated identifier of this vulnerability is VDB-216915.
CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 5.4
Published: 2022-12-27T23:15Z
Repository: https://github.com/openmrs/openmrs-module-appointmentscheduling
Language: Java
Files changed: api/src/main/java/org/openmrs/module/appointmentscheduling/validator/AppointmentTypeValidator.java, api/src/main/resources/messages.properties, api/src/test/java/org/openmrs/module/appointmentscheduling/validator/AppointmentTypeValidatorTest.java
Commit date: 2020-06-09 21:50:44 +0300
Commit message: RA-1772: Fix stored xss is appointment scheduling (#32)


Diff: diff --git a/api/src/main/java/org/openmrs/module/appointmentscheduling/validator/AppointmentTypeValidator.java b/api/src/main/java/org/openmrs/module/appointmentscheduling/validator/AppointmentTypeValidator.java
index 18092a4..0934e9a 100644
--- a/api/src/main/java/org/openmrs/module/appointmentscheduling/validator/AppointmentTypeValidator.java
+++ b/api/src/main/java/org/openmrs/module/appointmentscheduling/validator/AppointmentTypeValidator.java
@@ -16,10 +16,11 @@ package org.openmrs.module.appointmentscheduling.validator;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.openmrs.annotation.Handler;
 import org.openmrs.module.appointmentscheduling.AppointmentType;
 import org.openmrs.module.appointmentscheduling.api.AppointmentService;
+import org.openmrs.web.WebUtil;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.validation.Errors;
 import org.springframework.validation.ValidationUtils;
 import org.springframework.validation.Validator;
@@ -80,10 +81,13 @@ public class AppointmentTypeValidator implements Validator {
 			errors.rejectValue("name", "appointmentscheduling.AppointmentType.nameDuplicated");
 		}
 		if (verifyIfNameHasMoreThan100Characters(appointmentType.getName())) {
 			errors.rejectValue("name", "appointmentscheduling.AppointmentType.longName.errorMessage");
 		}
+		if(verifyIfNameHasHtmlEncodableChars(appointmentType.getName())){
+			errors.rejectValue("name", "appointmentscheduling.AppointmentType.unsafeName.errorMessage");
+		}
 	}
 	
 	private boolean verifyIfNameHasMoreThan100Characters(String appointmentName) {
 		if (appointmentName != null) {
 			return (appointmentName.length() > 100) ? true : false;
@@ -108,7 +112,14 @@ public class AppointmentTypeValidator implements Validator {
 		if (description != null) {
 			return (description.length() > 1024) ? true : false;
 		}
 		return false;
 	}
+
+	private boolean verifyIfNameHasHtmlEncodableChars(String appointmentName) {
+		if(appointmentName != null){
+			return !WebUtil.escapeHTML(appointmentName).equals(appointmentName);
+		}
+		return false;
+	}
 	
 }
diff --git a/api/src/main/resources/messages.properties b/api/src/main/resources/messages.properties
index 8ffe67b..5d2c391 100644
--- a/api/src/main/resources/messages.properties
+++ b/api/src/main/resources/messages.properties
@@ -22,10 +22,11 @@ ${project.parent.artifactId}.AppointmentType.durationEmpty=Empty appointment typ
 ${project.parent.artifactId}.AppointmentType.duration=Duration
 ${project.parent.artifactId}.AppointmentType.duration.errorMessage = Duration must be a positive number
 ${project.parent.artifactId}.AppointmentType.steps.details=Appointment Type Details
 ${project.parent.artifactId}.AppointmentType.nameDuplicated=Name is duplicated
 ${project.parent.artifactId}.AppointmentType.longName.errorMessage= Name must have less than 100 characters
+${project.parent.artifactId}.AppointmentType.unsafeName.errorMessage= Name must not have HTML characters
 ${project.parent.artifactId}.AppointmentType.description.errorMessage = Description must have less than 1024 characters
 
 ${project.parent.artifactId}.AppointmentBlock.manage.title=Provider Scheduling
 ${project.parent.artifactId}.AppointmentBlock.list.title=Current Appointment Blocks
 ${project.parent.artifactId}.AppointmentBlock.column.select=Select
diff --git a/api/src/test/java/org/openmrs/module/appointmentscheduling/validator/AppointmentTypeValidatorTest.java b/api/src/test/java/org/openmrs/module/appointmentscheduling/validator/AppointmentTypeValidatorTest.java
index e67098b..0360d01 100644
--- a/api/src/test/java/org/openmrs/module/appointmentscheduling/validator/AppointmentTypeValidatorTest.java
+++ b/api/src/test/java/org/openmrs/module/appointmentscheduling/validator/AppointmentTypeValidatorTest.java
@@ -92,11 +92,21 @@ public class AppointmentTypeValidatorTest {
 		
 		appointmentTypeValidator.validate(appointmentTypeLongName, errors);
 		
 		Mockito.verify(errors).rejectValue("name", "appointmentscheduling.AppointmentType.longName.errorMessage");
 	}
-	
+
+	@Test
+	public void mustRejectAppointmentTypeNameWithXSS() throws Exception {
+		String evilName = "<script>alert(1)</script>";
+		AppointmentType appointmentTypeEvilName = new AppointmentType(evilName, "", 10);
+
+		appointmentTypeValidator.validate(appointmentTypeEvilName, errors);
+
+		Mockito.verify(errors).rejectValue("name", "appointmentscheduling.AppointmentType.unsafeName.errorMessage");
+	}
+
 	@Test
 	public void mustRejectAppointmentTypeDescriptionWithMoreThan1024Characters() throws Exception {
 		String longDescription = StringUtils.repeat("*", 1025);
 		AppointmentType appointmentTypeLongDuration = new AppointmentType("long description", longDescription, 10);
 		

Fixed code: import org.openmrs.web.WebUtil;
		if(verifyIfNameHasHtmlEncodableChars(appointmentType.getName())){
			errors.rejectValue("name", "appointmentscheduling.AppointmentType.unsafeName.errorMessage");
		}

	private boolean verifyIfNameHasHtmlEncodableChars(String appointmentName) {
		if(appointmentName != null){
			return !WebUtil.escapeHTML(appointmentName).equals(appointmentName);
		}
		return false;
	}
${project.parent.artifactId}.AppointmentType.unsafeName.errorMessage= Name must not have HTML characters

	@Test
	public void mustRejectAppointmentTypeNameWithXSS() throws Exception {
		String evilName = "<script>alert(1)</script>";
		AppointmentType appointmentTypeEvilName = new AppointmentType(evilName, "", 10);

		appointmentTypeValidator.validate(appointmentTypeEvilName, errors);

		Mockito.verify(errors).rejectValue("name", "appointmentscheduling.AppointmentType.unsafeName.errorMessage");
	}