diff --git a/common/data/pom.xml b/common/data/pom.xml index ea4d8cb2d8..3067eba918 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -36,6 +36,14 @@ + + javax.validation + validation-api + + + org.owasp.antisamy + antisamy + org.slf4j slf4j-api diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/AdminSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/AdminSettings.java index 9389a2f3d6..356af5dab7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/AdminSettings.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/AdminSettings.java @@ -18,11 +18,13 @@ package org.thingsboard.server.common.data; import org.thingsboard.server.common.data.id.AdminSettingsId; import com.fasterxml.jackson.databind.JsonNode; +import org.thingsboard.server.common.data.validation.NoXss; public class AdminSettings extends BaseData { private static final long serialVersionUID = -7670322981725511892L; - + + @NoXss private String key; private transient JsonNode jsonValue; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ContactBased.java b/common/data/src/main/java/org/thingsboard/server/common/data/ContactBased.java index 9af8ddb736..a333591e53 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ContactBased.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ContactBased.java @@ -17,19 +17,28 @@ package org.thingsboard.server.common.data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.id.UUIDBased; +import org.thingsboard.server.common.data.validation.NoXss; @EqualsAndHashCode(callSuper = true) public abstract class ContactBased extends SearchTextBasedWithAdditionalInfo implements HasName { private static final long serialVersionUID = 5047448057830660988L; - + + @NoXss protected String country; + @NoXss protected String state; + @NoXss protected String city; + @NoXss protected String address; + @NoXss protected String address2; + @NoXss protected String zip; + @NoXss protected String phone; + @NoXss protected String email; public ContactBased() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java index e40ab84925..f6f49bb33b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java @@ -20,13 +20,13 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty.Access; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; - -import com.fasterxml.jackson.databind.JsonNode; +import org.thingsboard.server.common.data.validation.NoXss; public class Customer extends ContactBased implements HasTenantId { private static final long serialVersionUID = -1599722990298929275L; - + + @NoXss private String title; private TenantId tenantId; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java index aef34d6306..bdda77216d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java @@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.validation.NoXss; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -37,8 +38,11 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen private TenantId tenantId; private CustomerId customerId; + @NoXss private String name; + @NoXss private String type; + @NoXss private String label; private DeviceProfileId deviceProfileId; private transient DeviceData deviceData; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java index 8b24cefa71..44c1c4b0ac 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java @@ -24,7 +24,9 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.validation.NoXss; +import javax.validation.Valid; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -36,17 +38,22 @@ import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalIn public class DeviceProfile extends SearchTextBased implements HasName, HasTenantId { private TenantId tenantId; + @NoXss private String name; + @NoXss private String description; private boolean isDefault; private DeviceProfileType type; private DeviceTransportType transportType; private DeviceProfileProvisionType provisionType; private RuleChainId defaultRuleChainId; + @NoXss private String defaultQueueName; + @Valid private transient DeviceProfileData profileData; @JsonIgnore private byte[] profileDataBytes; + @NoXss private String provisionDeviceKey; public DeviceProfile() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java index 55ec23a9b3..e8b48ee23a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.objects.TelemetryEntityView; +import org.thingsboard.server.common.data.validation.NoXss; /** * Created by Victor Basanets on 8/27/2017. @@ -39,7 +40,9 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo private EntityId entityId; private TenantId tenantId; private CustomerId customerId; + @NoXss private String name; + @NoXss private String type; private TelemetryEntityView keys; private long startTimeMs; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java b/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java index 41dc37ec72..b6adf6cf65 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java @@ -20,13 +20,16 @@ import com.fasterxml.jackson.annotation.JsonProperty; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantProfileId; +import org.thingsboard.server.common.data.validation.NoXss; @EqualsAndHashCode(callSuper = true) public class Tenant extends ContactBased implements HasTenantId { private static final long serialVersionUID = 8057243243859922101L; - + + @NoXss private String title; + @NoXss private String region; private TenantProfileId tenantProfileId; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java b/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java index 3796ec856d..a0fefea6cc 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java @@ -23,6 +23,7 @@ import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; +import org.thingsboard.server.common.data.validation.NoXss; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -34,7 +35,9 @@ import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalIn @Slf4j public class TenantProfile extends SearchTextBased implements HasName { + @NoXss private String name; + @NoXss private String description; private boolean isDefault; private boolean isolatedTbCore; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/User.java b/common/data/src/main/java/org/thingsboard/server/common/data/User.java index 5792d23887..420aff71ce 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/User.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/User.java @@ -24,7 +24,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.security.Authority; -import com.fasterxml.jackson.databind.JsonNode; +import org.thingsboard.server.common.data.validation.NoXss; @EqualsAndHashCode(callSuper = true) public class User extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId, HasCustomerId { @@ -35,7 +35,9 @@ public class User extends SearchTextBasedWithAdditionalInfo implements H private CustomerId customerId; private String email; private Authority authority; + @NoXss private String firstName; + @NoXss private String lastName; public User() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java index 6209a11a49..f9d64cb712 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java @@ -15,12 +15,15 @@ */ package org.thingsboard.server.common.data.asset; -import com.fasterxml.jackson.databind.JsonNode; import lombok.EqualsAndHashCode; -import org.thingsboard.server.common.data.*; +import org.thingsboard.server.common.data.HasCustomerId; +import org.thingsboard.server.common.data.HasName; +import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.validation.NoXss; @EqualsAndHashCode(callSuper = true) public class Asset extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId, HasCustomerId { @@ -29,8 +32,11 @@ public class Asset extends SearchTextBasedWithAdditionalInfo implements private TenantId tenantId; private CustomerId customerId; + @NoXss private String name; + @NoXss private String type; + @NoXss private String label; public Asset() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmCondition.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmCondition.java index bbe20bdcaa..dee683d242 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmCondition.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmCondition.java @@ -17,15 +17,15 @@ package org.thingsboard.server.common.data.device.profile; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; -import org.thingsboard.server.common.data.query.KeyFilter; +import javax.validation.Valid; import java.util.List; -import java.util.concurrent.TimeUnit; @Data @JsonIgnoreProperties(ignoreUnknown = true) public class AlarmCondition { + @Valid private List condition; private AlarmConditionSpec spec; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilter.java index 86aafc19e5..b6dc9da8ba 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilter.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilter.java @@ -18,13 +18,19 @@ package org.thingsboard.server.common.data.device.profile; import lombok.Data; import org.thingsboard.server.common.data.query.EntityKeyValueType; import org.thingsboard.server.common.data.query.KeyFilterPredicate; +import org.thingsboard.server.common.data.validation.NoXss; + +import javax.validation.Valid; @Data public class AlarmConditionFilter { + @Valid private AlarmConditionFilterKey key; private EntityKeyValueType valueType; + @NoXss private Object value; + @Valid private KeyFilterPredicate predicate; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilterKey.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilterKey.java index 33ee0b0628..a82d2e362f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilterKey.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilterKey.java @@ -16,11 +16,13 @@ package org.thingsboard.server.common.data.device.profile; import lombok.Data; +import org.thingsboard.server.common.data.validation.NoXss; @Data public class AlarmConditionFilterKey { private final AlarmConditionKeyType type; + @NoXss private final String key; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmRule.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmRule.java index f04c75d75f..208cd03347 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmRule.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmRule.java @@ -16,13 +16,18 @@ package org.thingsboard.server.common.data.device.profile; import lombok.Data; +import org.thingsboard.server.common.data.validation.NoXss; + +import javax.validation.Valid; @Data public class AlarmRule { + @Valid private AlarmCondition condition; private AlarmSchedule schedule; // Advanced + @NoXss private String alarmDetails; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java index 99dc5c8cd4..ee9bdfb30f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java @@ -17,7 +17,9 @@ package org.thingsboard.server.common.data.device.profile; import lombok.Data; import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import org.thingsboard.server.common.data.validation.NoXss; +import javax.validation.Valid; import java.util.List; import java.util.TreeMap; @@ -25,9 +27,12 @@ import java.util.TreeMap; public class DeviceProfileAlarm { private String id; + @NoXss private String alarmType; + @Valid private TreeMap createRules; + @Valid private AlarmRule clearRule; // Hidden in advanced settings diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileData.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileData.java index b709a691b9..1bf8a88a3a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileData.java @@ -17,6 +17,7 @@ package org.thingsboard.server.common.data.device.profile; import lombok.Data; +import javax.validation.Valid; import java.util.List; @Data @@ -25,6 +26,7 @@ public class DeviceProfileData { private DeviceProfileConfiguration configuration; private DeviceProfileTransportConfiguration transportConfiguration; private DeviceProfileProvisionConfiguration provisionConfiguration; + @Valid private List alarms; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/query/DynamicValue.java b/common/data/src/main/java/org/thingsboard/server/common/data/query/DynamicValue.java index 2fa0a4d36a..6f824155de 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/query/DynamicValue.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/query/DynamicValue.java @@ -18,6 +18,7 @@ package org.thingsboard.server.common.data.query; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; import lombok.RequiredArgsConstructor; +import org.thingsboard.server.common.data.validation.NoXss; @Data @RequiredArgsConstructor @@ -27,6 +28,7 @@ public class DynamicValue { private T resolvedValue; private final DynamicValueSourceType sourceType; + @NoXss private final String sourceAttribute; private final boolean inherit; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/query/FilterPredicateValue.java b/common/data/src/main/java/org/thingsboard/server/common/data/query/FilterPredicateValue.java index 8bb227ab66..9228718b83 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/query/FilterPredicateValue.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/query/FilterPredicateValue.java @@ -20,15 +20,21 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; import lombok.Getter; +import org.thingsboard.server.common.data.validation.NoXss; + +import javax.validation.Valid; @Data public class FilterPredicateValue { @Getter + @NoXss private final T defaultValue; @Getter + @NoXss private final T userValue; @Getter + @Valid private final DynamicValue dynamicValue; public FilterPredicateValue(T defaultValue) { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/query/StringFilterPredicate.java b/common/data/src/main/java/org/thingsboard/server/common/data/query/StringFilterPredicate.java index fffe38cd57..d3a09813e3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/query/StringFilterPredicate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/query/StringFilterPredicate.java @@ -17,10 +17,13 @@ package org.thingsboard.server.common.data.query; import lombok.Data; +import javax.validation.Valid; + @Data public class StringFilterPredicate implements SimpleKeyFilterPredicate { private StringOperation operation; + @Valid private FilterPredicateValue value; private boolean ignoreCase; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java index 330b7de82d..f2610696f6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java @@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.validation.NoXss; @Data @EqualsAndHashCode(callSuper = true) @@ -35,6 +36,7 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo im private static final long serialVersionUID = -5656679015121935465L; private TenantId tenantId; + @NoXss private String name; private RuleNodeId firstRuleNodeId; private boolean root; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/validation/NoXss.java b/common/data/src/main/java/org/thingsboard/server/common/data/validation/NoXss.java new file mode 100644 index 0000000000..2ecc737fee --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/validation/NoXss.java @@ -0,0 +1,34 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.validation; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@Constraint(validatedBy = {}) +public @interface NoXss { + String message() default "field value is malformed"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/dao/pom.xml b/dao/pom.xml index 5b7f0f0954..e8bd09bc66 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -107,6 +107,14 @@ com.fasterxml.jackson.core jackson-databind + + org.hibernate.validator + hibernate-validator + + + org.glassfish + javax.el + org.springframework spring-context @@ -194,6 +202,11 @@ hsqldb test + + org.junit.jupiter + junit-jupiter-params + test + org.springframework spring-context-support diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java index e8b8eaf009..ff8e79efc2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java @@ -17,29 +17,50 @@ package org.thingsboard.server.dao.service; import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; +import org.hibernate.validator.HibernateValidator; +import org.hibernate.validator.HibernateValidatorConfiguration; +import org.hibernate.validator.cfg.ConstraintMapping; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.validation.NoXss; import org.thingsboard.server.dao.TenantEntityDao; import org.thingsboard.server.dao.exception.DataValidationException; +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Set; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; @Slf4j public abstract class DataValidator> { private static final Pattern EMAIL_PATTERN = Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}$", Pattern.CASE_INSENSITIVE); + private static Validator fieldsValidator; + + static { + initializeFieldsValidator(); + } + public void validate(D data, Function tenantIdFunction) { try { if (data == null) { throw new DataValidationException("Data object can't be null!"); } + + List validationErrors = validateFields(data); + if (!validationErrors.isEmpty()) { + throw new IllegalArgumentException("Validation error: " + String.join(", ", validationErrors)); + } + TenantId tenantId = tenantIdFunction.apply(data); validateDataImpl(tenantId, data); if (data.getId() == null) { @@ -81,6 +102,14 @@ public abstract class DataValidator> { return emailMatcher.matches(); } + private List validateFields(D data) { + Set> constraintsViolations = fieldsValidator.validate(data); + return constraintsViolations.stream() + .map(ConstraintViolation::getMessage) + .distinct() + .collect(Collectors.toList()); + } + protected void validateNumberOfEntitiesPerTenant(TenantId tenantId, TenantEntityDao tenantEntityDao, long maxEntities, @@ -111,4 +140,13 @@ public abstract class DataValidator> { throw new DataValidationException("Provided json structure is different from stored one '" + actualNode + "'!"); } } + + private static void initializeFieldsValidator() { + HibernateValidatorConfiguration validatorConfiguration = Validation.byProvider(HibernateValidator.class).configure(); + ConstraintMapping constraintMapping = validatorConfiguration.createConstraintMapping(); + constraintMapping.constraintDefinition(NoXss.class).validatedBy(NoXssValidator.class); + validatorConfiguration.addMapping(constraintMapping); + + fieldsValidator = validatorConfiguration.buildValidatorFactory().getValidator(); + } } 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 new file mode 100644 index 0000000000..e16aebbfea --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/NoXssValidator.java @@ -0,0 +1,57 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service; + +import com.google.common.io.Resources; +import lombok.extern.slf4j.Slf4j; +import org.owasp.validator.html.AntiSamy; +import org.owasp.validator.html.Policy; +import org.owasp.validator.html.PolicyException; +import org.owasp.validator.html.ScanException; +import org.thingsboard.server.common.data.validation.NoXss; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +@Slf4j +public class NoXssValidator implements ConstraintValidator { + private static final AntiSamy xssChecker = new AntiSamy(); + private static Policy xssPolicy; + + @Override + public void initialize(NoXss constraintAnnotation) { + if (xssPolicy == null) { + try { + xssPolicy = Policy.getInstance(Resources.getResource("xss-policy.xml")); + } catch (Exception e) { + log.error("Failed to set xss policy: {}", e.getMessage()); + } + } + } + + @Override + public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) { + if (!(value instanceof String) || ((String) value).isEmpty() || xssPolicy == null) { + return true; + } + + try { + return xssChecker.scan((String) value, xssPolicy).getNumberOfErrors() == 0; + } catch (ScanException | PolicyException e) { + return false; + } + } +} diff --git a/dao/src/main/resources/xss-policy.xml b/dao/src/main/resources/xss-policy.xml new file mode 100644 index 0000000000..6ea6660d2b --- /dev/null +++ b/dao/src/main/resources/xss-policy.xml @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + g + grin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file 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 new file mode 100644 index 0000000000..8463e722cb --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/NoXssValidatorTest.java @@ -0,0 +1,52 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import javax.validation.ConstraintValidatorContext; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.Mockito.mock; + +public class NoXssValidatorTest { + private static NoXssValidator validator; + + @BeforeAll + public static void beforeAll() { + validator = new NoXssValidator(); + validator.initialize(null); + } + + @ParameterizedTest + @ValueSource(strings = { + "aboba666", + "909090909", + "qwertyyyy", + "bambam", + "

Link!!!

1221", + "

Please log in to proceed

Username:

Password:



", + " ", + "123 bebe", + }) + public void testIsNotValid(String stringWithXss) { + boolean isValid = validator.isValid(stringWithXss, mock(ConstraintValidatorContext.class)); + assertFalse(isValid); + } + +} diff --git a/dao/src/test/resources/xss-policy.xml b/dao/src/test/resources/xss-policy.xml new file mode 100644 index 0000000000..6ea6660d2b --- /dev/null +++ b/dao/src/test/resources/xss-policy.xml @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + g + grin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index d9af0abf12..5530bdf179 100755 --- a/pom.xml +++ b/pom.xml @@ -47,6 +47,7 @@ 0.7.0 2.2.0 4.12 + 5.7.1 1.7.7 1.2.3 3.3.3 @@ -113,6 +114,10 @@ 1.0.2TB 3.4.0 7.54.2 + 6.0.13.Final + 3.0.0 + 2.0.1.Final + 1.6.2 @@ -1261,6 +1266,12 @@ ${junit.version} test + + org.junit.jupiter + junit-jupiter-params + ${jupiter.version} + test + org.dbunit dbunit @@ -1458,6 +1469,36 @@ + + org.hibernate.validator + hibernate-validator + ${hibernate-validator.version} + + + org.glassfish + javax.el + ${javax.el.version} + + + javax.validation + validation-api + ${javax.validation-api.version} + + + org.owasp.antisamy + antisamy + ${antisamy.version} + + + org.slf4j + * + + + com.github.spotbugs + spotbugs-annotations + + +