govspeak 7.1.1 → 8.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8b23cb6b80ef9b9d71a55b5cf0c1ffa1d33c3127ebac6290e7ed57de253439bd
4
- data.tar.gz: 5bbe38f1188c1159d7af520b22dc2fc011e95256c53be222170081220c11fa64
3
+ metadata.gz: 5e8536931b5ec2ad23bada1b0cc94dad05779dc3d9565bc8dbd6449a6161a7a2
4
+ data.tar.gz: a67ea45edba261236ce6974564a3704218e69dec860fea1e782d8d20af3922a4
5
5
  SHA512:
6
- metadata.gz: 13e3cbc3237ad2b739ff8d984d35e040e1379e716791fb9ed7d789b4d4ca9ce6398a53cb8bf095df68af80257aa5d1e2659803857f88677073576685da65df60
7
- data.tar.gz: 0f9dc64d2983ffa7c546d54f96c852500d92b2b01e2b48d163784b5d41a2a6e38202d8385665c264287b8035d0b8d96059a5fd0be7f065774d602b3caeefc84b
6
+ metadata.gz: b66013937116e786786f240b72c61dbbbcc1e09af0fa5bfdc4b83aeef73133eb46078565dfa5766d252a9558d0b10b2322673ca0720deef7fd69414ca2ad158c
7
+ data.tar.gz: dac0b4479fc4691e41356104409a93ec0802a698107c20ab17ab524042d6571d7da606111aa4b4635c4855ade81369fbfcd7db5dbb080205f3502b7423b6bfc5
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 8.0.1
2
+
3
+ * Add margin-bottom to embedded attachments ([#281](https://github.com/alphagov/govspeak/pull/281))
4
+
5
+ ## 8.0.0
6
+
7
+ * BREAKING: HTML style attribute and style element, which were never supposed to be available, are forbidden. [#279](https://github.com/alphagov/govspeak/pull/279)
8
+
1
9
  ## 7.1.1
2
10
 
3
11
  * Make image and attachment embedding syntax more consistent [#274](https://github.com/alphagov/govspeak/pull/274)
@@ -17,31 +17,13 @@ class Govspeak::HtmlSanitizer
17
17
  end
18
18
  end
19
19
 
20
- class TableCellTextAlignWhitelister
21
- def call(sanitize_context)
22
- return unless %w[td th].include?(sanitize_context[:node_name])
23
-
24
- node = sanitize_context[:node]
25
-
26
- # Kramdown uses text-align to allow table cells to be aligned
27
- # http://kramdown.gettalong.org/quickref.html#tables
28
- if invalid_style_attribute?(node["style"])
29
- node.remove_attribute("style")
30
- end
31
- end
32
-
33
- def invalid_style_attribute?(style)
34
- style && !style.match(/^text-align:\s*(center|left|right)$/)
35
- end
36
- end
37
-
38
20
  def initialize(dirty_html, options = {})
39
21
  @dirty_html = dirty_html
40
22
  @allowed_image_hosts = options[:allowed_image_hosts]
41
23
  end
42
24
 
43
25
  def sanitize(allowed_elements: [])
44
- transformers = [TableCellTextAlignWhitelister.new]
26
+ transformers = []
45
27
  if @allowed_image_hosts && @allowed_image_hosts.any?
46
28
  transformers << ImageSourceWhitelister.new(@allowed_image_hosts)
47
29
  end
@@ -60,21 +42,29 @@ class Govspeak::HtmlSanitizer
60
42
  end
61
43
 
62
44
  def sanitize_config(allowed_elements: [])
45
+ # We purposefully disable style elements which Sanitize::Config::RELAXED allows
46
+ elements = Sanitize::Config::RELAXED[:elements] - %w[style] +
47
+ %w[govspeak-embed-attachment govspeak-embed-attachment-link svg path].concat(allowed_elements)
48
+
63
49
  Sanitize::Config.merge(
64
50
  Sanitize::Config::RELAXED,
65
- elements: Sanitize::Config::RELAXED[:elements] + %w[govspeak-embed-attachment govspeak-embed-attachment-link svg path].concat(allowed_elements),
51
+ elements: elements,
66
52
  attributes: {
67
- :all => Sanitize::Config::RELAXED[:attributes][:all] + %w[role aria-label],
53
+ # We purposefully disable style attributes which Sanitize::Config::RELAXED allows
54
+ :all => Sanitize::Config::RELAXED[:attributes][:all] + %w[role aria-label] - %w[style],
68
55
  "a" => Sanitize::Config::RELAXED[:attributes]["a"] + [:data] + %w[draggable],
69
- "svg" => Sanitize::Config::RELAXED[:attributes][:all] + %w[xmlns width height viewbox focusable],
70
- "path" => Sanitize::Config::RELAXED[:attributes][:all] + %w[fill d],
56
+ "svg" => %w[xmlns width height viewbox focusable],
57
+ "path" => %w[fill d],
71
58
  "div" => [:data],
72
- # @TODO These style attributes can be removed once we've checked there
73
- # isn't hardcoded HTML in documents that uses them
59
+ # The style attributes are permitted here just for the ones Kramdown for table alignment
60
+ # we replace them in a post processor.
74
61
  "th" => Sanitize::Config::RELAXED[:attributes]["th"] + %w[style],
75
62
  "td" => Sanitize::Config::RELAXED[:attributes]["td"] + %w[style],
76
63
  "govspeak-embed-attachment" => %w[content-id],
77
64
  },
65
+ # The only styling we permit is text-align on table cells (which is the CSS kramdown
66
+ # generates), we can therefore only allow this one CSS property
67
+ css: { properties: %w[text-align] },
78
68
  )
79
69
  end
80
70
  end
@@ -59,6 +59,7 @@ module Govspeak
59
59
  attachment_html = GovukPublishingComponents.render(
60
60
  "govuk_publishing_components/components/attachment",
61
61
  attachment: attachment,
62
+ margin_bottom: 6,
62
63
  locale: govspeak_document.locale,
63
64
  )
64
65
  el.swap(attachment_html)
@@ -1,3 +1,3 @@
1
1
  module Govspeak
2
- VERSION = "7.1.1".freeze
2
+ VERSION = "8.0.1".freeze
3
3
  end
@@ -35,4 +35,15 @@ class GovspeakAttachmentLinkTest < Minitest::Test
35
35
  assert(root.css("p").size, 0)
36
36
  assert_match(/Attachment Title\s*test/, root.text)
37
37
  end
38
+
39
+ test "allows spaces and special characters in the identifier" do
40
+ attachment = {
41
+ id: "This is the name of my &%$@€? attachment",
42
+ url: "http://example.com/attachment.pdf",
43
+ title: "Attachment Title",
44
+ }
45
+
46
+ rendered = render_govspeak("[AttachmentLink: This is the name of my &%$@€? attachment]", [attachment])
47
+ assert_match(/Attachment Title/, rendered)
48
+ end
38
49
  end
@@ -21,6 +21,18 @@ class GovspeakAttachmentTest < Minitest::Test
21
21
  assert_match(/Attachment Title/, rendered)
22
22
  end
23
23
 
24
+ test "allows spaces and special characters in the identifier" do
25
+ attachment = {
26
+ id: "This is the name of my &%$@€? attachment",
27
+ url: "http://example.com/attachment.pdf",
28
+ title: "Attachment Title",
29
+ }
30
+
31
+ rendered = render_govspeak("[Attachment: This is the name of my &%$@€? attachment]", [attachment])
32
+ assert_match(/<section class="gem-c-attachment/, rendered)
33
+ assert_match(/Attachment Title/, rendered)
34
+ end
35
+
24
36
  test "only renders attachment when markdown extension starts on a new line" do
25
37
  attachment = {
26
38
  id: "attachment.pdf",
@@ -72,6 +72,15 @@ class GovspeakImagesTest < Minitest::Test
72
72
  end
73
73
  end
74
74
 
75
+ test "allows spaces and special characters in the identifier" do
76
+ image = build_image(id: "This is the name of my &%$@€? image")
77
+ given_govspeak "[Image: This is the name of my &%$@€? image]", images: [image] do
78
+ assert_html_output(
79
+ "<figure class=\"image embedded\"><div class=\"img\"><img src=\"http://example.com/image.jpg\" alt=\"my alt\"></div></figure>",
80
+ )
81
+ end
82
+ end
83
+
75
84
  test "Image is not inserted when it does not start on a new line" do
76
85
  given_govspeak "some text [Image:image-id]", images: [build_image] do
77
86
  assert_html_output("<p>some text [Image:image-id]</p>")
@@ -1,7 +1,7 @@
1
1
  require "test_helper"
2
2
 
3
- class GovspeakTableWithHeadersTest < Minitest::Test
4
- def expected_outcome
3
+ class GovspeakTablesTest < Minitest::Test
4
+ def expected_outcome_for_headers
5
5
  %(
6
6
  <table>
7
7
  <thead>
@@ -248,30 +248,44 @@ class GovspeakTableWithHeadersTest < Minitest::Test
248
248
  end
249
249
 
250
250
  test "Cells with |# are headers" do
251
- assert_equal document_body_with_hashes_for_all_headers.to_html, expected_outcome
251
+ assert_equal expected_outcome_for_headers, document_body_with_hashes_for_all_headers.to_html
252
252
  end
253
253
 
254
254
  test "Cells outside of thead with |# are th; thead still only contains th" do
255
- assert_equal document_body_with_hashes_for_row_headers.to_html, expected_outcome
255
+ assert_equal expected_outcome_for_headers, document_body_with_hashes_for_row_headers.to_html
256
256
  end
257
257
 
258
258
  test "Cells are given classes to indicate alignment" do
259
- assert_equal document_body_with_alignments.to_html, expected_outcome_for_table_with_alignments
259
+ assert_equal expected_outcome_for_table_with_alignments, document_body_with_alignments.to_html
260
+ end
261
+
262
+ test "Invalid alignment properties are dropped from cells" do
263
+ html = %(<table><tbody><tr><td style="text-align: middle">middle</td></tr></tbody></table>)
264
+ expected = "<table><tbody><tr><td>middle</td></tr></tbody></table>\n"
265
+
266
+ assert_equal expected, Govspeak::Document.new(html).to_html
267
+ end
268
+
269
+ test "Styles other than text-align are ignored on a table cell" do
270
+ html = %(<table><tbody><tr><td style="text-align: center; width: 100px;">middle</td></tr></tbody></table>)
271
+ expected = %(<table><tbody><tr><td class="cell-text-center">middle</td></tr></tbody></table>\n)
272
+
273
+ assert_equal expected, Govspeak::Document.new(html).to_html
260
274
  end
261
275
 
262
276
  test "Table headers with a scope of row are only in the first column of the table" do
263
- assert_equal document_body_with_table_headers_in_the_wrong_place.to_html, expected_outcome_for_table_headers_in_the_wrong_place
277
+ assert_equal expected_outcome_for_table_headers_in_the_wrong_place, document_body_with_table_headers_in_the_wrong_place.to_html
264
278
  end
265
279
 
266
280
  test "Table headers with a scope of row can have embedded links" do
267
- assert_equal document_body_with_table_headers_containing_links.to_html, expected_outcome_for_table_headers_containing_links
281
+ assert_equal expected_outcome_for_table_headers_containing_links, document_body_with_table_headers_containing_links.to_html
268
282
  end
269
283
 
270
284
  test "Table headers are not blank" do
271
- assert_equal document_body_with_blank_table_headers.to_html, expected_outcome_for_table_with_blank_table_headers
285
+ assert_equal expected_outcome_for_table_with_blank_table_headers, document_body_with_blank_table_headers.to_html
272
286
  end
273
287
 
274
288
  test "Table header superscript should parse" do
275
- assert_equal document_body_with_table_headers_containing_superscript.to_html, expected_outcome_for_table_with_table_headers_containing_superscript
289
+ assert_equal expected_outcome_for_table_with_table_headers_containing_superscript, document_body_with_table_headers_containing_superscript.to_html, expected_outcome_for_table_with_table_headers_containing_superscript
276
290
  end
277
291
  end
@@ -17,6 +17,16 @@ class HtmlSanitizerTest < Minitest::Test
17
17
  assert_equal "<a href=\"/\">Link</a>", Govspeak::HtmlSanitizer.new(html).sanitize
18
18
  end
19
19
 
20
+ test "disallow style attributes" do
21
+ html = '<a href="/" style="font-weight:bold">Link</a>'
22
+ assert_equal '<a href="/">Link</a>', Govspeak::HtmlSanitizer.new(html).sanitize
23
+ end
24
+
25
+ test "disallow style elements" do
26
+ html = "<style>h1 { color: pink; }</style><h1>Hi</h1>"
27
+ assert_equal "<h1>Hi</h1>", Govspeak::HtmlSanitizer.new(html).sanitize
28
+ end
29
+
20
30
  test "allow non-JS HTML content" do
21
31
  html = "<a href='foo'>"
22
32
  assert_equal "<a href=\"foo\"></a>", Govspeak::HtmlSanitizer.new(html).sanitize
@@ -79,16 +89,16 @@ class HtmlSanitizerTest < Minitest::Test
79
89
  assert_equal "<table><tbody><tr><th>thing</th><td>thing</td></tr></tbody></table>", Govspeak::HtmlSanitizer.new(html).sanitize
80
90
  end
81
91
 
82
- test "allows valid text-align properties on the style attribute for table cells and table headings" do
83
- %w[left right center].each do |alignment|
84
- html = "<table><thead><tr><th style=\"text-align: #{alignment}\">thing</th></tr></thead><tbody><tr><td style=\"text-align: #{alignment}\">thing</td></tr></tbody></table>"
85
- assert_equal html, Govspeak::HtmlSanitizer.new(html).sanitize
86
- end
92
+ test "allows text-align properties on the style attribute for table cells and table headings" do
93
+ html = "<table><thead><tr><th style=\"text-align: right\">thing</th></tr></thead><tbody><tr><td style=\"text-align: center\">thing</td></tr></tbody></table>"
94
+ assert_equal html, Govspeak::HtmlSanitizer.new(html).sanitize
95
+
96
+ input = "<table><thead><tr><th style=\"text-align: left;width: 100px;\">thing</th></tr></thead><tbody><tr><td style=\"text-align: center;background-color: blue;\">thing</td></tr></tbody></table>"
97
+ expected = "<table><thead><tr><th style=\"text-align: left;\">thing</th></tr></thead><tbody><tr><td style=\"text-align: center;\">thing</td></tr></tbody></table>"
98
+ assert_equal expected, Govspeak::HtmlSanitizer.new(input).sanitize
87
99
 
88
100
  [
89
101
  "width: 10000px",
90
- "text-align: middle",
91
- "text-align: left; width: 10px",
92
102
  "background-image: url(javascript:alert('XSS'))",
93
103
  "expression(alert('XSS'));",
94
104
  ].each do |style|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: govspeak
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.1.1
4
+ version: 8.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - GOV.UK Dev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-05-19 00:00:00.000000000 Z
11
+ date: 2023-07-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionview
@@ -190,14 +190,14 @@ dependencies:
190
190
  requirements:
191
191
  - - '='
192
192
  - !ruby/object:Gem::Version
193
- version: 4.10.0
193
+ version: 4.11.0
194
194
  type: :development
195
195
  prerelease: false
196
196
  version_requirements: !ruby/object:Gem::Requirement
197
197
  requirements:
198
198
  - - '='
199
199
  - !ruby/object:Gem::Version
200
- version: 4.10.0
200
+ version: 4.11.0
201
201
  - !ruby/object:Gem::Dependency
202
202
  name: simplecov
203
203
  requirement: !ruby/object:Gem::Requirement
@@ -302,7 +302,7 @@ files:
302
302
  - test/govspeak_link_extractor_test.rb
303
303
  - test/govspeak_link_test.rb
304
304
  - test/govspeak_structured_headers_test.rb
305
- - test/govspeak_table_with_headers_test.rb
305
+ - test/govspeak_tables_test.rb
306
306
  - test/govspeak_test.rb
307
307
  - test/govspeak_test_helper.rb
308
308
  - test/html_sanitizer_test.rb
@@ -327,7 +327,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
327
327
  - !ruby/object:Gem::Version
328
328
  version: '0'
329
329
  requirements: []
330
- rubygems_version: 3.4.13
330
+ rubygems_version: 3.4.16
331
331
  signing_key:
332
332
  specification_version: 4
333
333
  summary: Markup language for single domain
@@ -346,7 +346,7 @@ test_files:
346
346
  - test/govspeak_link_extractor_test.rb
347
347
  - test/govspeak_link_test.rb
348
348
  - test/govspeak_structured_headers_test.rb
349
- - test/govspeak_table_with_headers_test.rb
349
+ - test/govspeak_tables_test.rb
350
350
  - test/govspeak_test.rb
351
351
  - test/govspeak_test_helper.rb
352
352
  - test/html_sanitizer_test.rb