fix: strip HTML tags from derived excerpt

gsub(/<[^>]+>/, '') before stripping markdown markup, so auto-generated
excerpts don't leak raw HTML from post bodies.
This commit is contained in:
2026-06-25 22:06:49 +02:00
parent 4597c685da
commit d26d3b5a5b
3 changed files with 11 additions and 1 deletions
+4
View File
@@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `<pubDate>` in RSS feed items, derived from the post date. - `<pubDate>` in RSS feed items, derived from the post date.
- `<lastmod>` in sitemap entries for SEO. - `<lastmod>` in sitemap entries for SEO.
- In-memory rate limiting on the login endpoint: 10 attempts per 60 seconds per IP. - In-memory rate limiting on the login endpoint: 10 attempts per 60 seconds per IP.
- `<language>` element in the RSS feed channel.
- Periodic cleanup of stale `login_attempts` entries to prevent unbounded memory growth.
### Changed ### Changed
@@ -22,6 +24,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `admin.session_ttl` was defined but never read; it now controls - `admin.session_ttl` was defined but never read; it now controls
`Rack::Session::Cookie` expiration via `expire_after`. `Rack::Session::Cookie` expiration via `expire_after`.
- Derived post excerpts now strip HTML tags before stripping markdown markup,
avoiding raw HTML leaks in auto-generated excerpts.
## [0.1.0] — 2026-06-20 ## [0.1.0] — 2026-06-20
+1 -1
View File
@@ -112,7 +112,7 @@ module Volumen
.find { |para| !para.empty? && !para.start_with?("#") } .find { |para| !para.empty? && !para.start_with?("#") }
return "" if paragraph.nil? return "" if paragraph.nil?
stripped = paragraph.gsub(/[*_`>#]/, "").strip stripped = paragraph.gsub(/<[^>]+>/, "").gsub(/[*_`>#]/, "").strip
stripped.length > limit ? "#{stripped[0, limit].rstrip}" : stripped stripped.length > limit ? "#{stripped[0, limit].rstrip}" : stripped
end end
end end
+6
View File
@@ -30,6 +30,12 @@ class PostTest < Minitest::Test
assert_equal "The body paragraph.", post.excerpt assert_equal "The body paragraph.", post.excerpt
end end
def test_derived_excerpt_strips_html_tags
body = "<strong>Bold</strong> and <em>italic</em>."
post = Volumen::Post.new(metadata: { "slug" => "x" }, body: body)
assert_equal "Bold and italic.", post.excerpt
end
def test_summary_shape def test_summary_shape
post = Volumen::Post.new(metadata: { "slug" => "x", "title" => "X" }, body: "Body") post = Volumen::Post.new(metadata: { "slug" => "x", "title" => "X" }, body: "Body")
summary = post.summary summary = post.summary