From ecff604c8e8b82289bb6fae91b99426881b35616 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 1 Feb 2023 17:09:58 -0300 Subject: [PATCH 1/5] =?UTF-8?q?fix:=20eliminar=20el=20elemento=20sin=20src?= =?UTF-8?q?=20v=C3=A1lido=20en=20un=20solo=20lugar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/metadata_content.rb | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/app/models/metadata_content.rb b/app/models/metadata_content.rb index 9d3a1040..7598dc31 100644 --- a/app/models/metadata_content.rb +++ b/app/models/metadata_content.rb @@ -47,16 +47,13 @@ class MetadataContent < MetadataTemplate # Eliminar elementos sin src y comprobar su origen html.css(elements).each do |element| - unless element['src'] - element.remove - next - end - begin + raise URI::Error unless element['src'].present? + uri = URI element['src'] # No permitimos recursos externos - element.remove unless uri.scheme == 'https' && uri.hostname.end_with?(Site.domain) + raise URI::Error unless Rails.application.config.hosts.include?(uri.hostname) rescue URI::Error element.remove end From 1adfc91a7f413cedf999dea66d210f0eb8fb2cc8 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 1 Feb 2023 17:10:47 -0300 Subject: [PATCH 2/5] feat: convertir urls del panel en urls internas sutty/editor#59 --- app/models/metadata_content.rb | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/app/models/metadata_content.rb b/app/models/metadata_content.rb index 7598dc31..233d7448 100644 --- a/app/models/metadata_content.rb +++ b/app/models/metadata_content.rb @@ -54,6 +54,10 @@ class MetadataContent < MetadataTemplate # No permitimos recursos externos raise URI::Error unless Rails.application.config.hosts.include?(uri.hostname) + + element['src'] = convert_src_to_internal_path uri + + raise URI::Error if element['src'].blank? rescue URI::Error element.remove end @@ -71,4 +75,27 @@ class MetadataContent < MetadataTemplate html.to_s.html_safe end + + # Convierte una URI en una ruta interna del sitio actual + # + # XXX: No verifica si el archivo existe o no. Se supone que existe + # porque ya fue subido antes. + # + # @param uri [URI] + # @return [String,nil] + def convert_src_to_internal_path(uri) + signed_id = uri.path.split('/').fifth + blob = ActiveStorage::Blob.find_signed(signed_id) + + return unless blob + return unless blob.service_name == site.name + + blob_path = Pathname.new(blob.service.path_for(blob.key)).realpath + site_path = Pathname.new(site.path).realpath + + blob_path.relative_path_from(site_path).to_s + rescue ActiveSupport::MessageVerifier::InvalidSignature => e + ExceptionNotifier.notify_exception(e, data: { site: site.name }) + nil + end end From 04622751efcaff344818977213d548340e51ca8f Mon Sep 17 00:00:00 2001 From: f Date: Wed, 1 Feb 2023 17:12:09 -0300 Subject: [PATCH 3/5] feat: realiza el paso inverso sutty/editor#59 --- app/models/metadata_content.rb | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/app/models/metadata_content.rb b/app/models/metadata_content.rb index 233d7448..06c266a5 100644 --- a/app/models/metadata_content.rb +++ b/app/models/metadata_content.rb @@ -24,7 +24,11 @@ class MetadataContent < MetadataTemplate end def to_s - sanitizer.sanitize value, tags: [], attributes: [] + Nokogiri::HTML.fragment(value).tap do |html| + html.css('[src^="public/"]').each do |element| + element['src'] = convert_internal_path_to_src element['src'] + end + end.to_s end private @@ -76,6 +80,21 @@ class MetadataContent < MetadataTemplate html.to_s.html_safe end + # Convierte una ubicación local al sitio en una URL de ActiveStorage + # + # XXX: Por qué son tan díficiles de encontrar las rutas de AS + # + # @param path [String] + # @return [String] + def convert_internal_path_to_src(path) + key = path.split('/').second + blob = ActiveStorage::Blob.find_by(service_name: site.name, key: key) + + return unless blob + + "/rails/active_storage/blobs/#{blob.signed_id}/#{blob.filename}" + end + # Convierte una URI en una ruta interna del sitio actual # # XXX: No verifica si el archivo existe o no. Se supone que existe From 0781bee40ab84749997d70d583df75517b5e73d5 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 1 Feb 2023 17:12:27 -0300 Subject: [PATCH 4/5] fix: usar MetadataContent#to_s para procesar los adjuntos --- app/views/posts/attributes/_new_content.haml | 2 +- app/views/posts/show.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/posts/attributes/_new_content.haml b/app/views/posts/attributes/_new_content.haml index 520daff8..cbdf8f94 100644 --- a/app/views/posts/attributes/_new_content.haml +++ b/app/views/posts/attributes/_new_content.haml @@ -4,6 +4,6 @@ post: post, attribute: attribute, metadata: metadata .new-editor.content{ id: attribute } - = text_area_tag "#{base}[#{attribute}]", metadata.value.html_safe, + = text_area_tag "#{base}[#{attribute}]", metadata.to_s.html_safe, dir: dir, lang: locale, **field_options(attribute, metadata), class: 'd-none' diff --git a/app/views/posts/show.haml b/app/views/posts/show.haml index da3dd16a..c88905dc 100644 --- a/app/views/posts/show.haml +++ b/app/views/posts/show.haml @@ -38,4 +38,4 @@ - cache [metadata, I18n.locale] do %section.content.pb-3{ id: attr, dir: dir } - = @post.public_send(attr).value.html_safe + = @post.public_send(attr).to_s.html_safe From 4e4091f6c98e3d9e547252a5f94262bec344b952 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 1 Feb 2023 17:13:00 -0300 Subject: [PATCH 5/5] feat: limpieza de estilos esto permite guardar los nuevos estilos sin permitir otras cosas sutty/editor!1 --- app/models/metadata_content.rb | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/app/models/metadata_content.rb b/app/models/metadata_content.rb index 06c266a5..1664a18f 100644 --- a/app/models/metadata_content.rb +++ b/app/models/metadata_content.rb @@ -77,9 +77,40 @@ class MetadataContent < MetadataTemplate resource['controls'] = true end + # Elimina los estilos salvo los que asigne el editor + html.css('[style]').each do |element| + if (style = sanitize_style(element['style'])).present? + element['style'] = style + else + element.remove_attribute('style') + end + end + html.to_s.html_safe end + # Limpia estilos en base a una lista de permitidos + # + # @param style [String] + # @return [String] + def sanitize_style(style) + style.split(';').reduce({}) do |style_hash, style_string| + key, value = style_string.split(':', 2) + + style_hash[key] ||= value + style_hash + end.slice(*allowed_styles).map do |style_pair| + style_pair.join(':') + end.join(';') + end + + # Estilos permitidos + # + # @return [Array] + def allowed_styles + @allowed_styles ||= %w[text-align color background-color] + end + # Convierte una ubicación local al sitio en una URL de ActiveStorage # # XXX: Por qué son tan díficiles de encontrar las rutas de AS