vole
Clone
Read only, no credentials:
https://rodent.software/vole.git
Write over SSH with a registered key:
git clone ssh://git@rodent.software:2222/vole.git
A free and open-source forge that lives in your Git repo.
Fuzz the path validators and the Markdown sanitizer
5fce878d41da32448803f3a4f4d42c7841fba331 · Keenan Nemetz · 10d ago · parent d9f5a930
Fuzz the path validators and the Markdown sanitizer Two pieces of untrusted text decide where a file is written and what a browser runs: the validators that turn a request path, an artifact glob, or a site directory into a place under a root, and the markdown boundary where goldmark and bluemonday turn a pushed body into HTML a page trusts. The sanitizer assertions parse the output with an HTML parser rather than searching the string, because the difference is the whole question: a script tag rendered as text is a text node, and hand-written <script> looks the same to a substring search. The test asks what the browser will do: no script-shaped element, no on* attribute, and no scheme outside http, https, mailto. The path targets state the guarantee the way the caller uses it -- joined to a root, the result stays under that root -- and pin that cleaning is idempotent, so one page cannot become two cache keys. cleanPath's comment claimed dot segments were refused; they are resolved, and the comment now says so, which is what the property pins. Closes #98.
server/pages/pages.go +5−3
| @@ -298,9 +298,11 @@ func contentType(name string, body []byte) string { | |||
| 298 | 298 | return http.DetectContentType(body) | |
| 299 | 299 | } | |
| 300 | 300 | ||
| 301 | // cleanPath turns a URL path into a tree path, or refuses it. It is deliberately | ||
| 302 | // strict: the tree lookup cannot escape anywhere, but a path with dot segments is | ||
| 303 | // never a page anyone meant to publish. | ||
| 301 | // cleanPath turns a URL path into a tree path, or refuses it. Dot segments are | ||
| 302 | // resolved before the check (`../../etc/passwd` becomes `etc/passwd`), and what | ||
| 303 | // remains is refused unless every segment is a name. The tree lookup cannot escape | ||
| 304 | // anywhere either way, but a caller that builds a link, a cache key, or a log line | ||
| 305 | // from the result must not be handed a value with `..` in it. | ||
| 304 | 306 | func cleanPath(p string) (string, bool) { | |
| 305 | 307 | if strings.ContainsRune(p, 0) || strings.Contains(p, "\\") { | |
| 306 | 308 | return "", false | |