diff options
| author | Bryce Kwon <bryce@brycekwon.com> | |
|---|---|---|
| committer | Bryce Kwon <bryce@brycekwon.com> | |
| commit | ||
| parent | ||
| tree | ||
| download | ||
Lead a readme's relative links into the tree
A browser resolved them against the about page, where nothing lives
now that the page answers 404 below itself. They resolve the way
GitHub resolves a readme's links instead, from the readme's directory
and on the branch it was read from.
Diffstat (limited to 'custom/extensions')
| -rw-r--r-- | custom/extensions/about-render.lua | 61 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
1 file changed, 55 insertions, 6 deletions
diff --git a/custom/extensions/about-render.lua b/custom/extensions/about-render.lua index ecb2abe..a3932af 100644 --- a/custom/extensions/about-render.lua +++ b/custom/extensions/about-render.lua @@ -30,7 +30,7 @@ local max_bytes = 512 * 1024 -- markers cannot drive the recursive parser into a stack overflow. local max_depth = 24 -local filename, chunks = "", {} +local filename, tree_base, tree_query, chunks = "", "", "", {} -- Lua patterns backtrack, so "%s+$" costs the square of a run of spaces in @@ -108,11 +108,56 @@ local function safe_url(url) return url end +-- A readme percent-encodes what a link path cannot carry, and the path is +-- encoded again on its way out, so it is decoded first. An encoded NUL stays +-- encoded, since cgit's sinks would stop at a real one. +local function unescape_path(path) + return (path:gsub("%%(%x%x)", function(hex) + local byte = tonumber(hex, 16) + if byte > 0 then + return string.char(byte) + end + end)) +end + +-- The path in the tree that a relative link names. Like GitHub, it resolves +-- against the readme's own directory, or against the root when it starts +-- with a slash, and dot segments fold without climbing above the root. +local function tree_path(path) + if path:sub(1, 1) ~= "/" then + path = (filename:match("^(.*/)") or "") .. path + end + local segments = {} + for segment in path:gmatch("[^/]+") do + if segment == ".." then + segments[#segments] = nil + elseif segment ~= "." then + segments[#segments + 1] = segment + end + end + return table.concat(segments, "/") +end + +-- Writes a link target that safe_url passed. A relative one leads into the +-- tree at the ref the readme came from, keeping its fragment but not a +-- query, which the tree page would read as its own. A readme with no tree +-- behind it keeps its links as written. +local function emit_href(url) + if tree_base == "" or url:find("^#") or url:find("^%a[%w%+%.%-]*:") then + html_attr(url) + return + end + html_attr(tree_base) + html_url_path(tree_path(unescape_path(url:match("^[^?#]*")))) + html_attr(tree_query) + html_attr(url:match("#.*$") or "") +end + -- The page renders untrusted repository content. Every run of text goes out --- through cgit's html_txt, every attribute value through html_attr, and --- every link or image target through safe_url before html_attr. html() only --- ever gets literal tag scaffolding. +-- through cgit's html_txt, every attribute value through html_attr or, for a +-- path in the tree, html_url_path, and every link or image target through +-- safe_url first. html() only ever gets literal tag scaffolding. local emit_inline @@ -130,7 +175,7 @@ emit_inline = function(list) elseif kind == "link" then local target = safe_url(node.url) if target then - html("<a href='"); html_attr(target); html("'>") + html("<a href='"); emit_href(target); html("'>") emit_inline(node.kids) html("</a>") else @@ -632,8 +677,12 @@ for _, ext in ipairs({ "1", "2", "3", "4", "5", "6", "7", "8", "9", "man" }) do handlers[ext] = render_man end -function filter_open(name) +-- A readme tracked in the repository comes with the url of the tree page at +-- its ref and the query that follows a path there, both escaped for a url. +function filter_open(name, base, query) filename = name or "" + tree_base = base or "" + tree_query = query or "" chunks = {} end |
