Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
"enterMode": 2,
"entities": false,
"height": 200,
"allowedContent": true,
"forcePasteAsPlainText": false,
"extraAllowedContent": "*{color}; *[style]",
"disableAutoInline": true,
"extraPlugins": "colorbutton",
"toolbar": [
[
"PasteText",
Expand All @@ -32,6 +37,9 @@
"Subscript",
"Superscript"
],
[
"TextColor"
],
[
"NumberedList",
"BulletedList",
Expand Down
8 changes: 8 additions & 0 deletions cds/modules/deposit/static/json/cds_deposit/forms/video.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
"enterMode": 2,
"entities": false,
"height": 200,
"allowedContent": true,
"forcePasteAsPlainText": false,
"extraAllowedContent": "*{color}; *[style]",
"disableAutoInline": true,
"extraPlugins": "colorbutton",
"toolbar": [
[
"PasteText",
Expand All @@ -31,6 +36,9 @@
"Subscript",
"Superscript"
],
[
"TextColor"
],
[
"NumberedList",
"BulletedList",
Expand Down
24 changes: 18 additions & 6 deletions cds/modules/records/serializers/json.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,15 @@
has_read_record_permission,
)
from ..utils import HTMLTagRemover, parse_video_chapters, remove_html_tags
from marshmallow_utils.html import sanitize_html
from marshmallow_utils.html import sanitize_html, ALLOWED_HTML_ATTRS, ALLOWED_CSS_STYLES

CUSTOM_ALLOWED_ATTRS = {
**ALLOWED_HTML_ATTRS,
"span": ALLOWED_HTML_ATTRS.get("span", []) + ["style"],
"p": ALLOWED_HTML_ATTRS.get("p", []) + ["style"],
}

CUSTOM_ALLOWED_CSS = ALLOWED_CSS_STYLES + ["color"]


class CDSJSONSerializer(JSONSerializer):
Expand Down Expand Up @@ -60,7 +68,11 @@ def _sanitize_metadata(self, metadata):
if "description" in metadata:
description = metadata["description"]
description = self.html_tag_remover.unescape(description)
metadata["description"] = sanitize_html(description)
metadata["description"] = sanitize_html(
description,
attrs=CUSTOM_ALLOWED_ATTRS,
css_styles=CUSTOM_ALLOWED_CSS,
)

if "translations" in metadata:
for t in metadata["translations"]:
Expand Down Expand Up @@ -108,12 +120,12 @@ def preprocess_record(self, pid, record, links_factory=None):
except KeyError:
# ignore error if keys are missing in the metadata
pass
description = metadata.get('description', '')

description = metadata.get("description", "")
if description:
metadata['chapters'] = parse_video_chapters(description)
metadata["chapters"] = parse_video_chapters(description)
else:
metadata['chapters'] = []
metadata["chapters"] = []

return result

Expand Down
41 changes: 40 additions & 1 deletion cds/modules/records/serializers/schemas/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from marshmallow import RAISE, Schema, ValidationError, fields, validates_schema
from marshmallow.validate import Length
from marshmallow_utils.fields import SanitizedHTML
from marshmallow_utils.html import sanitize_html

from ...api import Keyword
from ...resolver import keyword_resolver
Expand Down Expand Up @@ -219,4 +220,42 @@ class RelatedIdentifiersSchema(Schema):
identifier = fields.Str(required=True)
scheme = fields.Str(required=True)
relation_type = fields.Str(required=True)
resource_type = fields.Str()
resource_type = fields.Str()


class SanitizedHTMLWithCSS(fields.String):
"""Enhanced SanitizedHTML supporting inline CSS sanitization.

Fully compatible with marshmallow_utils.fields.SanitizedHTML,
but adds CSS.
"""

def __init__(
self,
tags=None,
attrs=None,
css_styles=None,
*args,
**kwargs,
):
"""
:param tags: Allowed HTML tags.
:param attrs: Allowed HTML attributes per tag.
:param css_styles: List of allowed CSS properties (e.g., ["color"]).
"""
super().__init__(*args, **kwargs)

self.tags = tags
self.attrs = attrs
self.css_styles = css_styles

def _deserialize(self, value, attr, data, **kwargs):
"""Run bleach sanitize with CSS support."""
value = super()._deserialize(value, attr, data, **kwargs)

return sanitize_html(
value,
tags=self.tags,
attrs=self.attrs,
css_styles=self.css_styles,
)
Comment on lines +226 to +261

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

marshmallow_utils SanitizedHTML doesn't support css_styles

11 changes: 9 additions & 2 deletions cds/modules/records/serializers/schemas/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@
# 59 Temple Place, Suite 330, Boston, MA 02D111-1307, USA.
"""Project JSON schema."""

from cds.modules.records.serializers.json import (
CUSTOM_ALLOWED_ATTRS,
CUSTOM_ALLOWED_CSS,
)
from invenio_jsonschemas import current_jsonschemas
from marshmallow import Schema, fields, pre_load, post_load
from marshmallow_utils.fields import SanitizedHTML

from ....deposit.api import Project, deposit_video_resolver
from .common import (
Expand All @@ -32,6 +35,7 @@
KeywordsSchema,
LicenseSchema,
OaiSchema,
SanitizedHTMLWithCSS,
StrictKeysSchema,
TitleSchema,
TranslationsSchema,
Expand Down Expand Up @@ -77,7 +81,10 @@ class ProjectSchema(StrictKeysSchema):
_deposit = fields.Nested(ProjectDepositSchema, required=True)
_cds = fields.Nested(_CDSSSchema, required=True)
title = fields.Nested(TitleSchema, required=True)
description = SanitizedHTML()
description = SanitizedHTMLWithCSS(
attrs=CUSTOM_ALLOWED_ATTRS,
css_styles=CUSTOM_ALLOWED_CSS,
)
category = fields.Str(required=True)
type = fields.Str(required=True)
note = fields.Str()
Expand Down
22 changes: 12 additions & 10 deletions cds/modules/records/serializers/schemas/video.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@
# 59 Temple Place, Suite 330, Boston, MA 02D111-1307, USA.
"""Video JSON schema."""

from cds.modules.records.serializers.json import (
CUSTOM_ALLOWED_ATTRS,
CUSTOM_ALLOWED_CSS,
)
from invenio_jsonschemas import current_jsonschemas
from marshmallow import Schema, fields, pre_load, post_load
from marshmallow_utils.fields import SanitizedHTML
from ....deposit.api import Video
from ..fields.datetime import DateString
from .common import (
Expand All @@ -38,6 +41,7 @@
OaiSchema,
RelatedIdentifiersSchema,
RelatedLinksSchema,
SanitizedHTMLWithCSS,
StrictKeysSchema,
TitleSchema,
TranslationsSchema,
Expand Down Expand Up @@ -131,7 +135,9 @@ class VideoSchema(StrictKeysSchema):
contributors = fields.Nested(ContributorSchema, many=True, required=True)
copyright = fields.Nested(CopyrightSchema)
date = DateString(required=True)
description = SanitizedHTML(required=True)
description = SanitizedHTMLWithCSS(
attrs=CUSTOM_ALLOWED_ATTRS, css_styles=CUSTOM_ALLOWED_CSS, required=True
)
doi = DOI()
duration = fields.Str()
external_system_identifiers = fields.Nested(
Expand All @@ -147,7 +153,7 @@ class VideoSchema(StrictKeysSchema):
note = fields.Str()
publication_date = fields.Str()
recid = fields.Number()
legacy_recid =fields.Number()
legacy_recid = fields.Number()
related_links = fields.Nested(RelatedLinksSchema, many=True)
report_number = fields.List(fields.Str, many=True)
schema = fields.Str(attribute="$schema", data_key="$schema")
Expand All @@ -158,15 +164,11 @@ class VideoSchema(StrictKeysSchema):
_curation = fields.Nested(CurationSchema)
additional_titles = fields.List(fields.Nested(AdditionalTitlesSchema))
additional_descriptions = fields.List(fields.Nested(AdditionalDescriptionsSchema))
alternate_identifiers = fields.Nested(
AlternateIdentifiersSchema, many=True
)
related_identifiers = fields.Nested(
RelatedIdentifiersSchema, many=True
)
alternate_identifiers = fields.Nested(AlternateIdentifiersSchema, many=True)
related_identifiers = fields.Nested(RelatedIdentifiersSchema, many=True)
collections = fields.List(fields.Str, many=True)
additional_languages = fields.List(fields.Str, many=True)

# Preservation fields
location = fields.Str()
original_source = fields.Str()
Expand Down