prosemirror_to_html 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 258fc1681186bbb3be3b85c5e2924233f9792ce35dacc0eacbdde9224f756d93
4
- data.tar.gz: 1b68a3cbf52aa3c0712d707ad589d291c760dc715baad0e26556c462de7bdf9a
3
+ metadata.gz: aa4a2d6f0e0e0478ae5eddd0f8024bfee0a3a87af1c7039f2e9b2e42d6b8dfce
4
+ data.tar.gz: 39f9b61a9ace8c399beb416e60e78a5b6a42102ebd3d5ae1a3841959d3aa3398
5
5
  SHA512:
6
- metadata.gz: 6018cc198040f13e4f9fefbb147fb4c226bfa08bf7c473c625e5a85e57caa092ca60bbf35cc0932a253fbf0c1807a6204efea6723e171f2312050971cc388274
7
- data.tar.gz: cbb61fbeec67cdce929c5f9c3f06b137e3f9ab9e617f94acd22ff0dfe320ce4f93eff3021111dde8ee681b730193c6058f363bad3d467fcc1a9426988a35c3d3
6
+ metadata.gz: cefafdf1e45d0f76695fc0aafe3b45f9f4d7df3fd4c14c4832f20d1122d813e1a303833084c70e2647d4727bffea5a7a6fe5a984ec3a5deacdf73e262b2b1ea2
7
+ data.tar.gz: ac1023217710f151bb0225796e3d1ebe97a3c3b2997d54d3160848411d58394ed5eef4403d90d242ebf1fa4a3dbaea76e7e2fa7546df25ccb588541d43efd646
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## [0.4.0] - 2026-08-12
2
+
3
+ ### Security
4
+
5
+ - Sanitize all rendered output with Loofah, stripping event-handler attributes, `javascript:` URLs, and other unsafe HTML that value escaping alone did not neutralize (attribute-name XSS)
6
+ - Clamp the heading `level` to a valid range (1-6) so a document-supplied value cannot steer the tag name (e.g. injecting a `<script>` element)
7
+
1
8
  ## [0.3.1] - 2026-02-23
2
9
 
3
10
  ### Fixed
data/Gemfile.lock CHANGED
@@ -1,15 +1,20 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- prosemirror_to_html (0.3.1)
4
+ prosemirror_to_html (0.4.0)
5
+ loofah
5
6
  nokogiri
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
9
10
  specs:
11
+ crass (1.0.7)
10
12
  diff-lcs (1.6.2)
13
+ loofah (2.25.2)
14
+ crass (~> 1.0.2)
15
+ nokogiri (>= 1.12.0)
11
16
  mini_portile2 (2.8.9)
12
- nokogiri (1.19.1)
17
+ nokogiri (1.19.4)
13
18
  mini_portile2 (~> 2.8.2)
14
19
  racc (~> 1.4)
15
20
  racc (1.8.1)
@@ -27,7 +32,7 @@ GEM
27
32
  diff-lcs (>= 1.2.0, < 2.0)
28
33
  rspec-support (~> 3.13.0)
29
34
  rspec-support (3.13.7)
30
- yard (0.9.38)
35
+ yard (0.9.44)
31
36
 
32
37
  PLATFORMS
33
38
  ruby
@@ -6,7 +6,7 @@ module ProsemirrorToHtml
6
6
  def tag
7
7
  [
8
8
  {
9
- tag: "h#{@node[:attrs][:level]}",
9
+ tag: "h#{level}",
10
10
  attrs: tag_attrs
11
11
  }
12
12
  ]
@@ -14,6 +14,12 @@ module ProsemirrorToHtml
14
14
 
15
15
  private
16
16
 
17
+ # Coerce and clamp the level to a valid heading (1-6) so a
18
+ # document-supplied value cannot steer the tag name.
19
+ def level
20
+ @node[:attrs][:level].to_i.clamp(1, 6)
21
+ end
22
+
17
23
  def tag_attrs
18
24
  attrs_hash = @node[:attrs].to_h
19
25
  attrs_hash.slice(*attrs_hash.keys - %i[level])
@@ -1,3 +1,3 @@
1
1
  module ProsemirrorToHtml
2
- VERSION = "0.3.1"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -25,6 +25,7 @@ require "prosemirror_to_html/nodes/table_header"
25
25
  require "prosemirror_to_html/nodes/table_row"
26
26
  require 'nokogiri'
27
27
  require 'json'
28
+ require "loofah"
28
29
 
29
30
  module ProsemirrorToHtml
30
31
  class Error < StandardError; end
@@ -94,7 +95,7 @@ module ProsemirrorToHtml
94
95
  html << render_node(node, prev_node, next_node)
95
96
  end
96
97
 
97
- html
98
+ sanitize(html)
98
99
  end
99
100
 
100
101
  def add_node(node)
@@ -143,6 +144,23 @@ module ProsemirrorToHtml
143
144
 
144
145
  private
145
146
 
147
+ # Serialize without libxml2's block-element auto-indentation (no FORMAT
148
+ # flag), so sanitizing does not reformat the renderer's compact output.
149
+ SANITIZE_SAVE_OPTIONS = Nokogiri::XML::Node::SaveOptions::AS_HTML |
150
+ Nokogiri::XML::Node::SaveOptions::NO_DECLARATION |
151
+ Nokogiri::XML::Node::SaveOptions::NO_EMPTY_TAGS
152
+
153
+ # Scrub the rendered HTML against Loofah's allowlist, stripping unsafe
154
+ # elements and attributes (event handlers, javascript: URLs, unknown tags)
155
+ # while keeping their text content. Mirrors Loofah::Helpers.sanitize but
156
+ # serializes compactly (see SANITIZE_SAVE_OPTIONS).
157
+ def sanitize(html)
158
+ fragment = Loofah.fragment(html)
159
+ fragment.scrub!(:strip)
160
+ fragment.xpath("./form").each(&:remove)
161
+ fragment.to_html(save_with: SANITIZE_SAVE_OPTIONS)
162
+ end
163
+
146
164
  def render_node(node, prev_node, next_node)
147
165
  html = ''
148
166
  render_klass = nil
@@ -42,5 +42,6 @@ Gem::Specification.new do |spec|
42
42
  spec.add_development_dependency "rspec", "~> 3.0"
43
43
  spec.add_development_dependency "yard"
44
44
  spec.add_dependency "nokogiri"
45
+ spec.add_dependency "loofah"
45
46
 
46
47
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prosemirror_to_html
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hans Lemuet
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: loofah
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
83
97
  description: Takes ProseMirror compatible JSON and outputs HTML.
84
98
  email:
85
99
  - hans@etamin.studio
@@ -148,7 +162,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
148
162
  - !ruby/object:Gem::Version
149
163
  version: '0'
150
164
  requirements: []
151
- rubygems_version: 4.0.3
165
+ rubygems_version: 4.0.16
152
166
  specification_version: 4
153
167
  summary: Takes ProseMirror compatible JSON and outputs HTML.
154
168
  test_files: []