Prevent XSS-attacks (fixes #2871)
This commit is contained in:
parent
872f05ec31
commit
d55d374211
@ -41,6 +41,7 @@ Core:
|
||||
- Replaced angular-csv-import through Papa Parse for csv parsing.
|
||||
- Added smooth projector scroll.
|
||||
- Added watching permissions in client and change the view immediately on changes.
|
||||
- Validate HTML strings from CKEditor against XSS attacks.
|
||||
|
||||
Motions:
|
||||
- Added adjustable line numbering mode (outside, inside, none) for each
|
||||
|
@ -1,4 +1,5 @@
|
||||
from openslides.utils.rest_api import Field, ModelSerializer, ValidationError
|
||||
from openslides.utils.validate import validate_html
|
||||
|
||||
from .models import (
|
||||
ChatMessage,
|
||||
@ -78,6 +79,10 @@ class ProjectorMessageSerializer(ModelSerializer):
|
||||
model = ProjectorMessage
|
||||
fields = ('id', 'message', )
|
||||
|
||||
def validate(self, data):
|
||||
data['message'] = validate_html(data.get('message', ''))
|
||||
return data
|
||||
|
||||
|
||||
class CountdownSerializer(ModelSerializer):
|
||||
"""
|
||||
|
@ -12,6 +12,7 @@ from openslides.utils.rest_api import (
|
||||
SerializerMethodField,
|
||||
ValidationError,
|
||||
)
|
||||
from openslides.utils.validate import validate_html
|
||||
|
||||
from .models import (
|
||||
Category,
|
||||
@ -257,6 +258,10 @@ class MotionChangeRecommendationSerializer(ModelSerializer):
|
||||
'text',
|
||||
'creation_time',)
|
||||
|
||||
def validate(self, data):
|
||||
data['text'] = validate_html(data.get('text', ''))
|
||||
return data
|
||||
|
||||
|
||||
class MotionSerializer(ModelSerializer):
|
||||
"""
|
||||
@ -305,6 +310,15 @@ class MotionSerializer(ModelSerializer):
|
||||
'log_messages',)
|
||||
read_only_fields = ('state', 'recommendation',) # Some other fields are also read_only. See definitions above.
|
||||
|
||||
def validate(self, data):
|
||||
data['text'] = validate_html(data.get('text', ''))
|
||||
data['reason'] = validate_html(data.get('reason', ''))
|
||||
validated_comments = []
|
||||
for comment in data.get('comments', []):
|
||||
validated_comments.append(validate_html(comment))
|
||||
data['comments'] = validated_comments
|
||||
return data
|
||||
|
||||
@transaction.atomic
|
||||
def create(self, validated_data):
|
||||
"""
|
||||
|
@ -1,4 +1,5 @@
|
||||
from openslides.utils.rest_api import ModelSerializer
|
||||
from openslides.utils.validate import validate_html
|
||||
|
||||
from .models import Topic
|
||||
|
||||
@ -10,3 +11,7 @@ class TopicSerializer(ModelSerializer):
|
||||
class Meta:
|
||||
model = Topic
|
||||
fields = ('id', 'title', 'text', 'attachments', 'agenda_item_id')
|
||||
|
||||
def validate(self, data):
|
||||
data['text'] = validate_html(data.get('text', ''))
|
||||
return data
|
||||
|
34
openslides/utils/validate.py
Normal file
34
openslides/utils/validate.py
Normal file
@ -0,0 +1,34 @@
|
||||
import bleach
|
||||
|
||||
allowed_tags = [
|
||||
'a', 'img', # links and images
|
||||
'p', 'span', 'blockquote', # text layout
|
||||
'strike', 'strong', 'u', 'em', 'sup', 'sub', 'pre', # text formatting
|
||||
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', # headings
|
||||
'ol', 'ul', 'li', # lists
|
||||
'table', 'caption', 'thead', 'tbody', 'th', 'tr', 'td', # tables
|
||||
]
|
||||
allowed_attributes = {
|
||||
'*': ['class', 'style'],
|
||||
'img': ['alt', 'src', 'title'],
|
||||
'a': ['href', 'title'],
|
||||
'th': ['scope'],
|
||||
}
|
||||
allowed_styles = [
|
||||
'color', 'background-color', 'height', 'width', 'text-align'
|
||||
]
|
||||
|
||||
|
||||
def validate_html(html):
|
||||
"""
|
||||
This method takes a string and escapes all non-whitelisted html entries.
|
||||
Every field of a model that is loaded trusted in the DOM should be validated.
|
||||
"""
|
||||
if isinstance(html, str):
|
||||
return bleach.clean(
|
||||
html,
|
||||
tags=allowed_tags,
|
||||
attributes=allowed_attributes,
|
||||
styles=allowed_styles)
|
||||
else:
|
||||
return html
|
@ -8,3 +8,4 @@ roman>=2.0,<2.1
|
||||
setuptools>=18.5,<33.0
|
||||
Twisted>=16.2,<16.4
|
||||
Whoosh>=2.7,<2.8
|
||||
bleach>=1.5.0,<1.6
|
||||
|
@ -235,7 +235,7 @@ class CreateMotion(TestCase):
|
||||
config['motions_comments'] = [
|
||||
{'name': 'comment1', 'public': True},
|
||||
{'name': 'comment2', 'public': False}]
|
||||
comments = ['comemnt1_sdpoiuffo3%7dwDwW&', 'comment2_iusd&D/TdskDWH&5DWas46WAd078']
|
||||
comments = ['comemnt1_sdpoiuffo3%7dwDwW)', 'comment2_iusd_D/TdskDWH(5DWas46WAd078']
|
||||
response = self.client.post(
|
||||
reverse('motion-list'),
|
||||
{'title': 'title_test_sfdAaufd56HR7sd5FDq7av',
|
||||
|
11
tests/unit/utils/test_validate.py
Normal file
11
tests/unit/utils/test_validate.py
Normal file
@ -0,0 +1,11 @@
|
||||
from unittest import TestCase
|
||||
|
||||
from openslides.utils.validate import validate_html
|
||||
|
||||
|
||||
class ValidatorTest(TestCase):
|
||||
def test_XSS_protection(self):
|
||||
data = 'tuveegi2Ho<a><p>tuveegi2Ho<script>kekj9(djwk</script></p>Boovai7esu</a>ee4Yaiw0ei'
|
||||
self.assertEqual(
|
||||
validate_html(data),
|
||||
'tuveegi2Ho<a><p>tuveegi2Ho<script>kekj9(djwk</script></p>Boovai7esu</a>ee4Yaiw0ei')
|
Loading…
Reference in New Issue
Block a user