- Enforce SLUG_REGEX (alphanumeric + dot/dash/underscore only) in creation_error to prevent path traversal (C-1) and stored XSS (C-3). - Add File.expand_path containment check in default_path_for as defense-in-depth. - Escape slug in form action attributes (list, form views). - Validate username in change_username using the same regex as create_user (C-4). - Escape username in settings form action attributes. - Treat submitting the current username as a no-op success instead of 'taken'.
59 lines
1.6 KiB
Ruby
59 lines
1.6 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "test_helper"
|
|
require "tmpdir"
|
|
require "fileutils"
|
|
|
|
class StoreTest < Minitest::Test
|
|
def setup
|
|
@dir = Dir.mktmpdir
|
|
File.write(File.join(@dir, "hello.md"), "+++\ntitle = \"Hello\"\n+++\n\nBody\n")
|
|
FileUtils.mkdir_p(File.join(@dir, "cs"))
|
|
File.write(File.join(@dir, "cs", "ahoj.md"), "+++\ntitle = \"Ahoj\"\n+++\n\nTelo\n")
|
|
end
|
|
|
|
def teardown
|
|
FileUtils.remove_entry(@dir)
|
|
end
|
|
|
|
def test_all_loads_every_post
|
|
assert_equal 2, Volumen::Store.new(@dir).all.length
|
|
end
|
|
|
|
def test_slug_falls_back_to_filename
|
|
slugs = Volumen::Store.new(@dir).all.map(&:slug).sort
|
|
assert_equal %w[ahoj hello], slugs
|
|
end
|
|
|
|
def test_lang_derived_from_subdirectory
|
|
store = Volumen::Store.new(@dir, default_lang: "en")
|
|
assert_equal "cs", store.find("ahoj").lang
|
|
assert_equal "en", store.find("hello").lang
|
|
end
|
|
|
|
def test_find_returns_nil_when_missing
|
|
assert_nil Volumen::Store.new(@dir).find("nope")
|
|
end
|
|
|
|
def test_all_langs_post_is_visible_in_any_language
|
|
File.write(File.join(@dir, "global.md"), <<~POST)
|
|
+++
|
|
title = "Global"
|
|
lang = "en"
|
|
all_langs = true
|
|
+++
|
|
|
|
Visible everywhere.
|
|
POST
|
|
store = Volumen::Store.new(@dir, default_lang: "en")
|
|
assert store.find("global", lang: "cs")
|
|
assert store.find("global", lang: "en")
|
|
end
|
|
|
|
def test_save_rejects_path_traversal_slug
|
|
store = Volumen::Store.new(@dir)
|
|
post = Volumen::Post.new(metadata: { "slug" => "../../etc/passwd" }, body: "x")
|
|
assert_raises(ArgumentError, "slug escapes content directory") { store.save(post) }
|
|
end
|
|
end
|