diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/SearchTextBasedWithAdditionalInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/SearchTextBasedWithAdditionalInfo.java index 3545b290bd..8d7fba5238 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/SearchTextBasedWithAdditionalInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/SearchTextBasedWithAdditionalInfo.java @@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.id.UUIDBased; +import org.thingsboard.server.common.data.validation.NoXss; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -36,6 +37,7 @@ import java.util.function.Consumer; public abstract class SearchTextBasedWithAdditionalInfo extends SearchTextBased implements HasAdditionalInfo { public static final ObjectMapper mapper = new ObjectMapper(); + @NoXss private transient JsonNode additionalInfo; @JsonIgnore private byte[] additionalInfoBytes; diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/NoXssValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/NoXssValidator.java index 5e61d75fea..31496b84d5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/NoXssValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/NoXssValidator.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.service; +import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; import org.owasp.validator.html.AntiSamy; import org.owasp.validator.html.Policy; @@ -48,12 +49,18 @@ public class NoXssValidator implements ConstraintValidator { @Override public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) { - if (!(value instanceof String) || ((String) value).isEmpty()) { + String stringValue; + if (value instanceof CharSequence || value instanceof JsonNode) { + stringValue = value.toString(); + } else { + return true; + } + if (stringValue.isEmpty()) { return true; } try { - return xssChecker.scan((String) value, xssPolicy).getNumberOfErrors() == 0; + return xssChecker.scan(stringValue, xssPolicy).getNumberOfErrors() == 0; } catch (ScanException | PolicyException e) { return false; } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/NoXssValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/NoXssValidatorTest.java index 36eb76edb5..5a4f3af90e 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/NoXssValidatorTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/NoXssValidatorTest.java @@ -15,23 +15,16 @@ */ package org.thingsboard.server.dao.service; -import org.junit.jupiter.api.BeforeAll; +import com.fasterxml.jackson.databind.node.TextNode; +import org.junit.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.asset.Asset; -import javax.validation.ConstraintValidatorContext; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.mockito.Mockito.mock; +import static org.assertj.core.api.Assertions.assertThatThrownBy; public class NoXssValidatorTest { - private static NoXssValidator validator; - - @BeforeAll - public static void beforeAll() { - validator = new NoXssValidator(); - validator.initialize(null); - } @ParameterizedTest @ValueSource(strings = { @@ -44,9 +37,25 @@ public class NoXssValidatorTest { " ", "123 bebe" }) - public void testIsNotValid(String stringWithXss) { - boolean isValid = validator.isValid(stringWithXss, mock(ConstraintValidatorContext.class)); - assertFalse(isValid); + public void givenEntityWithMaliciousPropertyValue_thenReturnValidationError(String maliciousString) { + Asset invalidAsset = new Asset(); + invalidAsset.setName(maliciousString); + + assertThatThrownBy(() -> { + ConstraintValidator.validateFields(invalidAsset); + }).hasMessageContaining("field value is malformed"); + } + + @Test + public void givenEntityWithMaliciousValueInAdditionalInfo_thenReturnValidationError() { + Asset invalidAsset = new Asset(); + String maliciousValue = "qwertyqwerty"; + invalidAsset.setAdditionalInfo(JacksonUtil.newObjectNode() + .set("description", new TextNode(maliciousValue))); + + assertThatThrownBy(() -> { + ConstraintValidator.validateFields(invalidAsset); + }).hasMessageContaining("field value is malformed"); } }